Pixel

Pixel Updates & Events

Server Updates & Events

Announcements

DEV Community

You Will Understand 10 Years of LLMs

My new video, In 19 Minutes, You'll Understand 10 Years of LLMs, follows one question through every era of language models: "What is the capital of France?" This post is the developer version. It goes through every chapter of the video with the actual terms and numbers. Where the video shows a model's output, we ran an open model ourselves, so you get the code to reproduce it. pip install torch transformers accelerate
All outputs below are what we got on an RTX 4070 Ti. Yours may differ slightly with hardware and precision. TL;DR timeline Year Milestone What it fixed What it left open 1948–51 Shannon's next-letter game "predict what comes next" as a measurable idea no machine could play it well 2013 word2vec words become vectors that capture meaning one vector per word, whatever the context 1997–2014 RNN, LSTM, seq2seq read sentences, translate one fixed-size summary per sentence 2014 attention (Bahdanau) the decoder looks back at every word still reads one word at a time 2017 the Transformer no recurrence: parallel training on GPUs writing is still one token at a time 2018–19 BERT, GPT-1, GPT-2 pretraining on raw text the model continues text, it doesn't answer 2020–22 scaling laws, GPT-3, Chinchilla predictable gains from size and data still not helpful 2022 InstructGPT / RLHF, ChatGPT a second training stage teaches it to answer serving at scale is expensive 2023–24 LLaMA, KV-cache sharing, mixture of experts cheaper, faster, longer contexts running out of human text 2024–25 o1, DeepSeek-R1 models learn to think before answering thinking helps, doesn't guarantee 2026 agents, hybrid attention plan, use tools, act 0. The quiz page (Experiment 1) Before chatbots, a language model's only job was to continue text. Ask GPT-2 XL (1.5B parameters, 2019): from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

name = "openai-community/gpt2-xl"
tok = AutoTokenizer.from_pretrained(name)
model = AutoModelForCausalLM.from_pretrained(name).eval()

ids = tok("What is the capital of France?", return_tensors="pt").input_ids
out = model.generate(ids, max_new_tokens=60, do_sample=False, pad_token_id=tok.eos_token_id)
print(tok.decode(out[0][ids.shape[1]:]))
The capital of France is Paris.

What is the capital of the United States?

The capital of the United States is Washington, D.C.

What is the capital of the United Kingdom?

The capital of the United Kingdom is London.

What
It's not answering; it's writing a quiz page. We'll come back to why. 1. Before 2017: the road to attention Shannon's game (1948, 1951) Claude Shannon asked how predictable English is. Letters don't come at random: counted in Pride and Prejudice, after a T comes an H 29% of the time; after a Q, a U 99.85% of the time. In 1948 he generated text that follows these odds with a book: pick a letter, open another page, find that letter, copy the one after it, repeat. Letter pairs give almost-words; word pairs give "The head and in frontal attack on an English writer that the character of this point…" Not English, but closer. The more context, the closer. In 1951 he made it a game with people: guess the next letter. On one passage they were right on the first try 79 times out of 102. He estimated that a letter of English carries about 1 bit of new information, against about 4.75 bits if letters were random. That's the objective LLMs still train on: predict the next token. Words become vectors (2013) A word ID (king = #4812) says nothing about meaning. word2vec (Mikolov et al., 2013) gives each word a vector of a few hundred numbers (300 in the classic setup), learned by predicting a word's neighbours over billions of words. Words used in similar contexts end up close, and the space has structure: king − man + woman ≈ queen (the nearest word, excluding the three inputs). The limit: one vector per word. The "bank" of a river and the bank with your money get the same vector. RNN, LSTM, seq2seq (1997–2014) A recurrent neural network reads one token at a ...

Powered by Readybot.io

**DEV Community** ## You Will Understand 10 Years of LLMs …

DEV Community

Appointment reminders in a Telegram bot without a scheduler library

When I added reminders to a small appointment bot, my first instinct was to reach for a job scheduler: one job per booking, fired at the right time. I ended up with something much smaller: one loop and two columns. Here is why, and how it works. The problem with one job per booking A job per booking means every change to a booking has to touch the scheduler too. A client cancels: remove the job. The owner moves an appointment: reschedule the job. The bot restarts: the in-memory jobs are gone unless you add a persistent job store. That is a second source of truth, and it will drift from the bookings table sooner or later. Poll the table instead The bookings table already knows everything needed. So the bot asks it one question every minute: which active bookings start soon and have not been reminded yet? async def reminder_loop(bot):
while True:
try:
due = store.due_reminders(now())
for kind, label in (("day", "tomorrow"), ("hours", "soon")):
for r in due[kind]:
try:
await bot.send_message(r["user_id"], f"Reminder: you have a booking {label}")
except Exception as e:
logging.warning("Reminder not delivered: %s", e)
except Exception:
logging.exception("Reminder loop error")
await asyncio.sleep(60)
It is started once next to polling: task = asyncio.create_task(reminder_loop(bot))
try:
await dp.start_polling(bot)
finally:
task.cancel()
Cancelled bookings drop out on their own because the query only looks at active rows. Moved bookings are picked up at their new time. After a restart nothing is lost, because nothing lived in memory. Two flags, not one The bot sends two reminders: a day before and two hours before. Each gets its own column, reminded_day and reminded_hours, both 0 by default. def due_reminders(self, now):
out = {"day": [], "hours": []}
for kind, col, delta in (("day", "reminded_day", timedelta(hours=24)),
("hours", "reminded_hours", timedelta(hours=2))):
rows = self.db.execute(
"SELECT * FROM bookings WHERE status='active' AND %s=0 "
"AND start>? AND start<=?" % col,
(now.isoformat(), (now + delta).isoformat())).fetchall()
with self.db:
for r in rows:
self.db.execute("UPDATE bookings SET %s=1 WHERE id=?" % col, (r["id"],))
out[kind] = rows
return out
The column name is interpolated into the SQL, which is safe here only because it comes from a fixed tuple in the code and never from user input. All values still go through parameters. Three details that matter Mark before you send. The flag is set when the row is selected, not after the message is delivered. If sending fails, the client misses one reminder. The other order risks sending the same reminder every minute while Telegram is returning errors, which is worse. Catch errors per message. A client can block the bot at any time, and send_message then raises. Without the inner try, one blocked user stops reminders for everyone after them in the list. Short-notice bookings. Someone books at 10:00 for 12:00. That booking is inside both windows at once, so a naive loop sends "tomorrow" and "soon" back to back. I still mark both flags, but drop the day-before message when the visit is less than three hours away: out["day"] = [r for r in out["day"]
if datetime.fromisoformat(r["start"]) - now > timedelta(hours=3)]
When this is not enough Polling every minute means a reminder can be up to a minute late, which is fine for appointments. It also assumes one bot process. With several workers you would need to claim rows atomically, for example with a single UPDATE ... WHERE reminded_day=0 RETURNING . For a bot serving one business, the loop above is enough. The full bot This reminder code is part of a complete appointment bot: booking flow, double-booking p...

Powered by Readybot.io*

**DEV Community** ## Appointment reminders in a Telegram bot without …

DEV Community

MCP Sampling: When the Server Calls the Client Model

Quick answer: Sampling is the reverse of a normal tool call. Instead of the client calling a server tool, the server sends a sampling/createMessage request asking the host to run its own LLM and return the result. The server supplies messages and model preferences, but the host chooses the actual model, controls cost and consent, and can decline the request. A server that needs language ability without holding its own model key uses sampling. The direction reverses Most of MCP flows one way: the host calls tools and reads resources the server exposes. Sampling flips that path. While handling a request, a server may need to summarize, classify, or draft text, and it may have no model access of its own. Rather than embedding a key or calling a separate provider, it asks the host to complete the message using the model the user already configured. The model choice, billing, and any approval stay with the host, while the server gets the generated text it needs. How a sampling request works The server sends a request containing the messages, an optional system prompt, and parameters such as max tokens and temperature. It can include model preferences expressed as hints for intelligence, speed, or cost, but it cannot name a specific model. The host receives the request, decides which model to use, and in many implementations surfaces the call for review because it spends tokens and runs content the server supplied. The host then calls the model and returns the generated message, or rejects the request. Sampling vs tools Dimension Tool call Sampling Direction Client calls server Server calls client Initiator Host/model Server Model choice Host picks the tool Host picks the model Typical use Run server logic Generate text for the server Consent Tool approval Sampling approval What the server can and cannot set The request gives the server real control over the task itself: it provides the message history, an optional system prompt, and limits such as max tokens and temperature, and it can state model preferences as hints. It cannot force a named model, cannot guarantee the host keeps those parameters, and cannot see which model the host ultimately selected unless the response reports it. This split is deliberate. The server owns the question and the context around it, while the host owns the resource that answers it, including the spend and the decision to expose that model to an external server. Designing around that boundary means the server should frame the prompt clearly enough to work across models rather than tuning it to one. Honest limits A server cannot assume a sampling request will be fulfilled. The host can reject it, select a different model than the hints suggest, or change the parameters, so the output varies across hosts and is not deterministic. Logic that needs a fixed answer should not depend on sampling, and the server should handle a declined request rather than treating it as an error it can retry blindly. The protocol defines the message shape, not how each host presents the prompt or whether it asks the user. To see the real behavior, run a live request and inspect what the host returns; Powerduck keeps the request and response visible so you can confirm the model selection and the exact message that comes back instead of assuming the hints were honored. Frequently asked questions Does the server choose the model? No. It sends preferences as hints, and the host selects the actual model and may ignore the hints. Why use sampling instead of a model API key? The server borrows the host's configured model, so it avoids storing keys and leaves cost and consent with the user. Can the host refuse? Yes. The host can decline the request, and the server should handle that path. Is sampling output predictable? No. Different hosts and models return different results, so it is not suited to deterministic logic. The bottom line Sampling lets a server request a completion from the model the host controls, with the host re...

Powered by Readybot.io

**DEV Community** ## MCP Sampling: When the Server Calls the …

DEV Community

Your AI-Built App Is Probably Leaking Data: A Practical Supabase Row Level Security Audit

Tuần này HN có thread gần 1000 điểm về việc các app làm bằng Lovable để lộ ảnh hộ chiếu và hơn 200K tin nhắn riêng tư. Đọc kỹ thì nguyên nhân không mới. Frontend gọi thẳng Supabase bằng anon key, mà bảng trong database thì không bật Row Level Security (RLS) hoặc có bật nhưng policy viết kiểu using (true). Ai mở DevTools, copy key ra rồi curl là đọc được toàn bộ dữ liệu. Mình từng review vài dự án của team và freelancer dùng Cursor, Lovable, Bolt để dựng MVP, và gặp lại đúng lỗi này. AI sinh code chạy được rất nhanh, nhưng nó tối ưu cho việc "chạy được" chứ không lo chuyện "chỉ đúng người mới đọc được". Bài này là checklist mình dùng để tự audit một app Supabase trong khoảng 30 phút. Vì sao anon key được public mà vẫn an toàn (khi có RLS) Nhiều bạn hoảng khi thấy anon key nằm trong JS bundle. Thật ra đó là thiết kế có chủ đích. Supabase dùng PostgREST để expose schema public thành REST API, còn anon key chỉ là một JWT với role: anon. Thứ quyết định ai được đọc dòng nào là RLS policy trong PostgreSQL, không phải cái key. flowchart LR
A[Browser - anon key + user JWT] --> B[PostgREST API]
B --> C{RLS enabled?}
C -- Không --> D[Trả về TOÀN BỘ bảng]
C -- Có --> E{Policy match auth.uid?}
E -- Có --> F[Chỉ trả dòng của user]
E -- Không --> G[Trả mảng rỗng]
H[service_role key] -.bypass RLS.-> D Có hai điểm chết người ở đây: RLS tắt: anon key thành chìa khóa vạn năng, đọc, sửa, xóa gì cũng được. service_role key lọt ra frontend: key này bypass RLS hoàn toàn. Mình đã thấy AI "fix lỗi permission denied" bằng cách thay anon key bằng service_role. Lỗi biến mất, còn dữ liệu thì mở toang. Nếu project dùng format key mới (sb_publishable_... và sb_secret_...) thì vẫn áp dụng y hệt: sb_secret_ tuyệt đối không được xuất hiện ở client. Bước 1: Tự tấn công app của mình bằng curl Cách nhanh nhất là đóng vai attacker. Lấy URL project và anon key trong tab Network của DevTools, rồi chạy: #!/usr/bin/env bash

audit_rls.sh - thử đọc từng bảng bằng anon key, không đăng nhập

SUPABASE_URL="https://xyzcompany.supabase.co"
ANON_KEY="eyJhbGciOi..."

1. Lấy danh sách bảng từ OpenAPI spec mà PostgREST tự expose

TABLES=$(curl -s "$SUPABASE_URL/rest/v1/" \
- H "apikey: $ANON_KEY" | jq -r '.definitions | keys[]')

2. Thử đọc 1 dòng từ mỗi bảng

for t in $TABLES; do
count=$(curl -s "$SUPABASE_URL/rest/v1/$t?select=*&limit=1" \
- H "apikey: $ANON_KEY" \
- H "Authorization: Bearer $ANON_KEY" | jq 'if type=="array" then length else 0 end')
[ "$count" -gt 0 ] && echo "[LEAK] $t đọc được khi chưa login"
done

3. Quét bundle build xem có lọt service_role / secret key không

grep -rEo 'eyJ[A-Za-z0-9_-]{20,}.[A-Za-z0-9_-]+.[A-Za-z0-9_-]+' dist/ | sort -u | \
while read -r jwt; do
echo "$jwt" | cut -d. -f2 | base64 -d 2>/dev/null | grep -q service_role \
&& echo "[CRITICAL] service_role key trong bundle!"
done
grep -rq 'sb_secret_' dist/ && echo "[CRITICAL] sb_secret_ key trong bundle!"
Cần jq (1.7+). Bảng nào in ra [LEAK] thì xử lý ngay. Lưu ý: bảng trả mảng rỗng chưa chắc đã an toàn, có thể chỉ do bảng đang trống. Bước 3 sẽ test kỹ hơn. Song song, vào SQL Editor chạy query này để liệt kê mọi bảng chưa bật RLS: select schemaname, tablename
from pg_tables
where schemaname = 'public' and rowsecurity = false;
Nếu đã cài Supabase CLI (v2.x), chạy thêm supabase db lint --linked và mở Security Advisor trong dashboard. Hai chỗ này bắt được lỗi rls_disabled_in_public và policy_exists_rls_disabled (có policy nhưng quên bật RLS, lỗi rất hay gặp). Bước 2: Bật RLS và viết policy cho đúng Đây là pattern mình dùng cho bảng messages, mỗi tin nhắn chỉ người gửi và người nhận được xem: alter table public.messages enable row level security;
- - Áp dụng cả cho table owner, tránh test bằng owner rồi tưởng là ổn
alter table public.messages force row level security;

    • SELECT: chỉ sender hoặc receiver
      create policy "messages_select_own" on public.messages
      for select to authenticated
      using ((select auth.uid()) in (sender_id, r...

      Powered by Readybot.io
**DEV Community** ## Your AI-Built App Is Probably Leaking Data: …

DEV Community

I Built Aven App Agent to Help Users Find Their Next Click

I Built Aven App Agent to Help Users Find Their Next Click Suggested DEV tags: ai, webdev, ux, showdev Paste the title into DEV's title field and the article below into the body. The YouTube embed uses DEV's supported embed syntax. A user opens your app with a simple goal. “Invite someone to my workspace.” They look at the dashboard. Open a menu. Try a different tab. Somewhere in your product, the right button exists. Finding it is the problem. I'm the founder of Aven App Agent, and I'm building an AI assistant that connects questions like this to guidance inside the product. Users can ask for help, read an answer, and choose a walkthrough that points them toward the controls they need. Start with the task the user wants to finish Take an invitation flow that crosses several parts of a website. Open Settings. Switch to Team. Select Invite members. Enter the details and send the invitation. Each individual screen can be straightforward while the complete path still takes effort to discover. The user thinks about inviting a person. The application organizes that task into pages, tabs, and fields. That gap is where Aven fits. The user asks, “How do I invite a teammate?” Aven can offer a guide that highlights the next relevant control. The user follows the steps in the application where the task happens. Here's an overview of the product. The engineering detail that shapes the experience A useful guide needs to know which page the user is on, which element a step refers to, and when that element is ready. Aven uses context the product team registers, including pages, elements, and workflows. The host application handles navigation and UI behavior. That gives developers explicit places to connect guidance to the routes and controls they own. How Aven's guided workflows work For a developer trying this in a product, I'd start with one complete task. Register its context, test the question that should lead to it, and walk through every step as a user would. Pay particular attention to route changes, loading states, permissions, and responsive layouts. A button that exists in one state may be absent in another. Those conditions deserve the same care as the instruction shown beside it. The Web SDK is available through npm. npm install @avenappagent/web
The setup flow is to initialize the SDK, register the relevant pages, elements, and workflows, then test the question and guide against your application's current state. The Web SDK documentation covers the configuration and registration methods. Aven Web SDK Knowledge Chat handles the questions that need an explanation Some questions are about understanding the product. “What is a workspace?” needs an explanation. “How do I invite someone?” may benefit from a guide. Aven's Knowledge Chat uses connected documentation, pages, and files to answer questions and show supporting sources. The aim is to keep relevant help close to the user's task, with a source they can consult for more detail. Give product teams several ways to build guides Writing a guide means choosing a target, explaining the action, and checking how the step appears in the product. Aven offers several authoring paths for that work. Chrome Visual Builder. Select an element on the live website and edit the guide through a left sidebar with templates and settings. Preview the tooltip against the actual page before publishing. GitHub import. Connect a selected repository with read-only access to generate draft guides. Review their content and targets on the site before publishing them. Mobile editor. Connect a test device, capture an app screen, and select a target in the editor. Build the guide step and preview it on the connected device. Each path brings the author back to the experience the user will see. Checking the target and the instruction together is part of building the guide. Watch the demos in different applications We've recorded Aven integration demos using three open-source projects with different interface...

Powered by Readybot.io

**DEV Community** ## I Built Aven App Agent to Help …

DEV Community

Bun for Beginners: JavaScript's All-in-One Runtime, Bundler, and Test Runner

Bun is a JavaScript runtime that wants to replace three tools at once: Node.js, your bundler, and your test runner. It is written in Zig, it starts fast, and it runs JavaScript and TypeScript directly — no build step for most code. If Node.js feels slow to start and your toolchain feels like five tools taped together, Bun is worth a look. The pitch is speed and simplicity. Bun starts in milliseconds, installs packages faster than npm, and runs TypeScript out of the box. A small server looks like this: // server.js
Bun.serve({
port: 3000,
fetch(req) {
return new Response("Hello from Bun!");
},
});
Run it with bun run server.js and you have a working server. No framework, no build step, no configuration. Bun also has a built-in bundler (bun build), a test runner (bun test with Jest-compatible syntax), and a package manager (bun install) that is noticeably faster than npm on large projects. Bun is compatible with most Node.js APIs and most npm packages, so you can usually drop it into an existing project and see if it works. That compatibility is the reason it is taken seriously — it is not a new ecosystem, it is a faster way to run the ecosystem you already have. The limits are honest. Bun is young, and some Node.js packages still break on it, especially ones that rely on native bindings or obscure APIs. The community is smaller than Node's, so answers are harder to find. And in production, Node.js has a decade of battle-testing that Bun is still earning. For a side project or a new service, Bun is a joy. For a mission-critical system, most teams still choose Node.js for now. Start today with one step: install Bun, write the server above, and time how long it takes to start versus the same server in Node.js. Then run bun test on a small test file. The speed difference is the argument — and it is hard to unsee.

Powered by Readybot.io

**DEV Community** ## Bun for Beginners: JavaScript's All-in-One Runtime, Bundler, …

DEV Community

I built an API to generate OG images, screenshots, and PDFs without managing Puppeteer/Playwright infrastructure

I built Ogify: one API for OG images, website screenshots, HTML-to-image, and PDFs When I started building web products, I kept running into the same annoying problem: I needed to generate social preview images, take screenshots of pages, and create PDFs—but I did not want to maintain a headless-browser fleet just to do it. So I built Ogify: an API that turns URLs, HTML, and reusable templates into OG images, screenshots, and PDFs. Try it: https://www.ogify.online Docs: https://www.ogify.online/docs The problem A typical product needs several visual-rendering features: Generate an Open Graph image when a user shares a blog post, product page, or profile. Capture a website screenshot for previews, reports, dashboards, or support tickets. Convert HTML into a PNG, JPEG, or WebP image. Generate invoices, certificates, reports, or printable pages as PDFs. Serve the generated asset from a stable URL inside your app, CMS, email, or automation workflow. You can build this yourself with Puppeteer or Playwright, but then you have to manage browser binaries, rendering workers, timeouts, memory, fonts, caching, scaling, and failures. That is a lot of infrastructure for something that is often just “make this URL into an image.” Ogify handles the rendering side through one API, so you can focus on your product instead of operating browser infrastructure. What Ogify can do Ogify supports several common rendering workflows: Feature What it does URL to screenshot Capture a live webpage as PNG, JPEG, or WebP, with viewport, full-page, and element-capture options. HTML to image Render arbitrary HTML and CSS into an image. OG image templates Create reusable branded social-card templates and render them with dynamic data. URL or HTML to PDF Generate hosted PDFs for invoices, reports, certificates, and web pages. Smart social card Turn one URL into extracted metadata, a screenshot, and a branded card. Composite rendering Combine a live website capture with a branded social card. Hosted assets Return a hosted URL that can be used directly in metadata, email, CMS content, or automation pipelines. Quick example: generate an OG image Here is the basic idea: send a template name and the dynamic content you want rendered, then receive an image URL. bash curl -X POST https://api.ogify.online/v1/render/template \ -H "x-api-key: $OGIFY_KEY" \ -H "Content-Type: application/json" \ -d '{ "template": "blog", "title": "My Post" }' The response includes a hosted image URL: json { "image_url": "https://api.ogify.online/i/..." } That URL can then be used directly in your page metadata: xml The API is designed so the generated asset can be used directly in a product, metadata tag, CMS, email, CI/CD workflow, or webhook-based automation. A practical use case Imagine a blogging platform where every post needs a social preview image. Without Ogify, you might need to: Build an HTML template for the card. Launch a headless browser. Render the template with post data. Take a screenshot. Store and serve the image. Keep the browser workers healthy and scale them as traffic grows. With Ogify, your backend can request the image and use the returned hosted URL immediately. The same idea works for screenshots of user dashboards, PDF invoices, certificates, and branded social cards. Security and API keys API keys should stay server-side. Ogify’s documentation covers API keys, headers, key rotation, and security practices, so keep the key in an environment variable or secret manager rather than exposing it in frontend code. Why I built it I wanted a simple developer workflow: Create an API key. Call one endpoint to render a URL, HTML, PDF, or template. Use the returned hosted asset URL in my application. That is the core idea behind Ogify: make visual rendering something you can add with an API call, rather than something that requires maintaining your own rendering infrastructure. What I would love feedback on I am especially interested in feedback from developers building...

Powered by Readybot.io

**DEV Community** ## I built an API to generate OG …

DEV Community

CVE-2026-129433: Dokploy 9.9 — The Access Control Is the Member List

CVE-2026-129433 is a CVSS 9.9 authenticated RCE in Dokploy, the self-hosted Docker deployment platform. Vector: AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H The PR:L requires an account. The S:C scope change crosses from the web app to the Docker host. The result is root on the host — every container, every env var, every secret the team deployed. Why PR:L is thin on a team platform: Dokploy's access model is an org member list. The precondition is the product's normal usage. What to do: Upgrade to the fixed release (confirm versions from the Dokploy advisory) Audit the org member list — remove stale accounts, shared logins; enable MFA Treat the Docker host as tier zero — segment it, monitor it If exposed on a vulnerable version: rotate all secrets reachable from the host The access control for this CVE is the member list. The audit is part of the fix the patch doesn't perform. Full write-up: https://threataft.com/articles/cve-2026-129433-dokploy-authenticated-rce-docker

Powered by Readybot.io

**DEV Community** ## CVE-2026-129433: Dokploy 9.9 — The Access Control …

DEV Community

How to make an AI drama with the same cast in every episode

Disclosure: an AI assistant (Claude) drafted this story for the RefVid team. Every image below is a still from our own AI-generated clips or a drawn character sheet, not a photograph of a real person. Most AI video tools start each clip from one picture or from words alone, so a face drifts a little every time. That is fine for one clip and fatal for a drama, where viewers have to recognise the lead in episode ten. This is the method we use to keep one cast across episodes, with the real clips and prompts behind it. Save each character once Give every character a small set of reference photos instead of a single portrait. One clear portrait is enough to start, and three to five hold a face better: straight on, three-quarter from each side, and one waist-up in the costume they wear all season. Invented characters work the same way, as a portrait from an image tool or a drawn character sheet. Mia and Leo, two invented actors: one of the eight reference photos saved once and reused in every scene. Every scene you generate then starts from those saved photos, so the model sees the same faces from several angles in each new clip. Several characters can share one shot, each tied to their own photos, so a couple, a rival and a boss keep their faces apart. The cast stays in your account, and the next episode starts from the same saved characters instead of new uploads. Shoot one scene at a time Write the episode as four to eight scenes of 10 to 15 seconds, with the spoken lines in quotes. Here is a scene prompt that works, the one behind the café clip below: Rainy evening in a quiet café: Mia and Leo sit across a small table, untouched coffee between them. Wide shot, moody window light. Close-up of Mia, eyes steady, saying quietly: ‘You knew all along, didn’t you?’ Close-up of Leo, looking down, then up, and answering: ‘I was going to tell you tonight.’ The result is a 10-second vertical clip with spoken dialogue and room sound, made on Kling 3.0 Omni from the same saved photos. A still from the café scene: Mia and Leo, 10 seconds, vertical, with dialogue. A cast that survives a whole series The same method scales past one scene. Our own cartoon series, GuaPi, has four episodes, and each of its four characters was drawn once and saved once. Every episode draws on those same character sheets, so Henry, Alice, GuaPi the goose and Xiaobai look the same in episode four as in episode one. Henry, from the GuaPi character sheets. GuaPi the goose, saved once and used in all four episodes. A still from GuaPi Episode 3: 36 seconds on Kling 4, drawn from the same character sheets. For a beat with no dialogue, a plain action line is enough. From Episode 1: The little fan sparks and bursts with a pop: a thick cloud of black smoke explodes out of it, the blades jam and stop, the umbrella jolts; Henry’s grin turns into wide-eyed panic. Keep the drawn style. End every episode on an open action, such as a phone lighting up or a door opening, and start the next one from it. Checks before you post an episode Compare the costumes with last time, spell the names the same way in every caption, and check the faces side by side with episode one. Switch on the AI label wherever the platform asks for it. Get consent from every real person in the cast, and never use the face of someone who has not agreed. Keep the scene prompts and the saved cast together, so a scene that needs a redo can be made again with the same characters. The whole method, with both prompts, the step-by-step table and the answers to the usual questions, is on one page: the RefVid AI drama generator page. Louis Chi

Powered by Readybot.io

**DEV Community** ## How to make an AI drama with …

DEV Community

How to get a company's open jobs straight from Greenhouse, Lever or Ashby

You have a list of companies and want their open roles in one table, with no browser and no HTML parsing. Most careers pages built on Greenhouse, Lever or Ashby are rendered from a public JSON endpoint that anyone can call, and the three are similar enough to merge into one shape. The three endpoints The board token is the last part of the careers URL: job-boards.greenhouse.io/, jobs.lever.co/, jobs.ashbyhq.com/. An unknown token returns 404 on all three. Greenhouse: GET https://boards-api.greenhouse.io/v1/boards//jobs. One request returns the whole board (674 jobs for the board I tried, in about a second). You get id, title, location.name, first_published, updated_at and absolute_url. Descriptions need ?content=true, which made that same response about 10 MB. Pay is not in the list; /jobs/?pay_transparency=true adds pay_input_ranges when the company publishes one, one request per job. Lever: GET https://api.lever.co/v0/postings/?mode=json. Descriptions are included, createdAt is epoch milliseconds, and the location sits in categories.location. Large boards are slow: an 85-posting board took about 10 seconds today. Lever also pages with skip and limit, and has an EU host, api.eu.lever.co. Ashby: GET https://api.ashbyhq.com/posting-api/job-board/. One request for the board. Add ?includeCompensation=true for pay ranges where published, which made one board's response about 3 MB. Normalise them import json, urllib.request
from datetime import datetime, timezone

def get(url):
req = urllib.request.Request(url, headers={"User-Agent": "careers-feed-example/1.0 (personal script)"})
with urllib.request.urlopen(req, timeout=60) as r:
return json.load(r)

def greenhouse(board):
for j in get(f"https://boards-api.greenhouse.io/v1/boards/{board}/jobs")["jobs"]:
yield dict(ats="greenhouse", id=str(j["id"]), title=j["title"].strip(),
location=j["location"]["name"], posted=j["first_published"],
url=j["absolute_url"])

def lever(site):
for j in get(f"https://api.lever.co/v0/postings/{site}?mode=json"):
yield dict(ats="lever", id=j["id"], title=j["text"].strip(),
location=j["categories"].get("location"), posted=datetime.fromtimestamp(j["createdAt"] / 1000, timezone.utc).isoformat(),
url=j["hostedUrl"])

def ashby(board):
for j in get(f"https://api.ashbyhq.com/posting-api/job-board/{board}")["jobs"]:
yield dict(ats="ashby", id=j["id"], title=j["title"].strip(),
location=j["location"], posted=j["publishedAt"], url=j["jobUrl"])
Output (one run): greenhouse 674 jobs; first: 3P Revenue Accounting & Reporting | San Francisco, CA | Seattle, WA | 2026-10-19T11:17:24-04:00
lever 10 jobs; first: Customer Success Manager | Atlanta, Georgia | 2026-08-31T18:12:19.703000+00:00
ashby 176 jobs; first: Security Engineer, Cloud | New York, NY (HQ) | 2026-04-07T17:12:35.753+00:00
The .strip() on titles is there because one Ashby title came back with a leading space. The Lever board here is Lever's own demo board, leverdemo, which is why it has only 10 jobs. Spot new and removed postings Postings have stable ids, so a daily diff is a set comparison. Save the previous ids, print what is new, and print what disappeared. Save the first script as ats.py; this one imports from it. import json
from ats import greenhouse, lever

jobs = {f'{j["ats"]}:{j["id"]}': j for j in [greenhouse("anthropic"), lever("leverdemo")]}

try:
old = json.load(open("jobs_last.json"))
except FileNotFoundError:
old = None

if old is not None:
for key in jobs.keys() - old.keys():
print("NEW ", jobs[key]["title"], "|", jobs[key]["url"])
for key in old.keys() - jobs.keys():
print("REMOVED", old[key]["title"], "|", old[key]["url"])
json.dump(jobs, open("jobs_last.json", "w"))
print(len(jobs), "open jobs saved")
To test it I ran it once, deleted one entry from jobs_la...

Powered by Readybot.io

**DEV Community** ## How to get a company's open jobs …

DEV Community

Datadog Alternatives: 10 Options and How to Pick One

If you are searching for a Datadog alternative, the problem is usually the bill, and a new tool is 1 of 5 ways to fix it: Bottom line There are 5 routes away from your current Datadog bill, and only 4 of them change the tool. Stay and cut the bill: fewer indexed logs, fewer tag combinations, a commitment that fits Another commercial platform: New Relic, Dynatrace, Honeycomb, Elastic Managed open source: Grafana Cloud, SigNoz Cloud Self-hosted open source: Prometheus, Grafana, Loki and Tempo, or SigNoz, or VictoriaMetrics Your cloud's own monitoring: Amazon CloudWatch, Google Cloud Observability The 5 routes away from your Datadog bill. Only 4 of them change the tool. Here is where I would start, by situation: Your situation Start with The bill grew, the tool works Route 1, then compare You are leaving, with under 20 engineers and nobody to run a monitoring stack Grafana Cloud or SigNoz Cloud Data has to stay in your own cloud account Self-hosted Grafana stack or SigNoz You debug requests across many services all day Honeycomb A large company that wants one vendor and one contract Dynatrace or New Relic One cloud, a few services, a small budget CloudWatch or Google Cloud Observability Whatever you pick, the price of the new tool is the smaller cost. The bigger one is moving your instrumentation, your dashboards and your monitors. So do one thing before you pick: send your telemetry through OpenTelemetry. After that, changing the tool is a change of address. All prices here are public list prices in USD, checked on November 15, 2026 against each vendor's pricing page. Your contract can differ. Is the Datadog bill the reason you are reading this? Our senior engineers assess your setup, plan the work and do it hands-on, paid by the hour with no lock-in. Book a free 30-minute consultation. Find your situation: The renewal is close and you need a number: 500 GB of logs and 20,000 metrics, priced on each tool You want to keep Datadog and pay less: Route 1 You want open source and no servers to run: Route 3 You already decided to leave: move to OpenTelemetry first, then run both side by side Know what you use before you compare tools Datadog is a price list of separate products. So is every alternative. Before you open a comparison page, write down which of these you use today: Infrastructure metrics and the integrations that feed them Application tracing (APM) Logs: search, pipelines, archives Dashboards and monitors Synthetic tests and real user monitoring (RUM) On-call and incident tooling Then write down why you want to leave. There are 4 honest reasons: The bill: it grows faster than the system Lock-in: your code and your agents speak one vendor's format Data location: telemetry must stay in your account or your region Fit: you use 2 products out of 20 and pay for a platform Reason = route. A bill problem starts at Route 1. A data location problem starts at Route 4. (A fifth reason exists: someone saw a nicer dashboard at a conference. It is the most expensive one.) Route 1: Cut the Datadog bill and stay This is the cheapest route, and the one that gets skipped. In my Datadog pricing breakdown, a setup of 50 hosts costs $4,360 a month at list price. 3 changes bring it to $2,953: Exclusion filters on the log indexes, so you ingest everything and index a part of it Fewer queryable tags on the custom metrics that multiply APM only on the hosts that send traces you read None of them needs a new tool, a migration or a retrained team. Do this first even if you plan to leave. The filters and the tag cleanup carry over: every tool on this page bills you for volume in some unit. Stay when the bill after the cleanup is one you can defend. Leave when it is still too high, or when the reason was never the bill. Route 2: Another commercial platform These are full platforms run by a vendor. You swap one bill for another, with a different unit. Tool What sets the bill List price Fits New Relic GB ingested, plus full platform users 100 GB a ...

Powered by Readybot.io

**DEV Community** ## Datadog Alternatives: 10 Options and How to …

DEV Community

Write the test plan before your coding agent writes code

When a coding agent writes code faster than you can read it, reading every line stops working as review, and approved tests have to carry most of that load. So define the tests before the agent writes any implementation, read them yourself, and require every commit to pass them. Your attention moves from reading diffs to deciding what "done" means. Anush Elangovan is Corporate VP of AI Software at AMD. Before we recorded his April 2026 episode, he told me he was generating 250,000 lines of code a week with AI agents. Lines of code isn't a measure of success, but it shows the throughput problem: no human reviews that volume line by line. By the time we talked, he said a week had earlier come to about 4.2 billion tokens, a check that morning showed 6.5 billion consumed, and he could no longer keep track of lines. Why write tests before the agent writes code? Because the tests are the only fixed point while the implementation churns. Anush held this view before agents existed: Yeah, so my philosophy is, even before agentic coding, I used to advise people and friends and colleagues, 80% of your time should be spent on tests and writing tests. Even before you write your first line of code, you should have a test framework. Anush Elangovan, Corporate VP of AI Software at AMD, on Chain of Thought ep 56 250,000 Lines of Code/Week: In… - Chain of Thought | AI Agents, Infrastructure & Engineering - Apple Podcasts Podcast Episode · Chain of Thought | AI Agents, Infrastructure & Engineering · April 22 · 51m podcasts.apple.com With agents, the tests also do the job a reviewer used to do. When he doesn't like something about the output, he turns it into a stronger gate instead of arguing with the agent: Yep, testing is the code review and testing is kind of the gate of your sandbox. If you have a complaint about anything in the box, fix the sandbox, right? Anush Elangovan, Corporate VP of AI Software at AMD, on Chain of Thought ep 56 That includes non-functional requirements. For his virtual GPU simulator he set a performance metric, each dispatch executing in 50 microseconds of simulator time. When a change fails that test, the agent has to find another approach. How do you check the tests an agent proposes? Read them before you approve them. Anush's example: an agent wanted to implement GPU demand paging in one go. He stopped it and told it to write 100 tests covering combinations and corner cases first. Some of the tests were repetitive, with parameters changed but not the thing being tested. After reading them, he was satisfied that if the set passed, the feature was good. Things to look for in an agent-written test list: The same assertion with only literal values changed. Missing negative cases, or platforms you named in the plan but no test covers. Mocks that remove the behavior you care about, which make passing cheap. His reason for the scrutiny is that agents optimize for the gate: they try to just reward hack their way towards that test. So all you have to think of is like, they cannot reward hack that, right? Like you're putting up gates that they have to pass through. Anush Elangovan, Corporate VP of AI Software at AMD, on Chain of Thought ep 56 A minimal sketch to adapt. Test cases are plain records with a content hash, so an agent can't keep an approved test ID while weakening its assertions. def near_duplicates(cases):
"""cases: list of dicts with 'id', 'target' and 'params' keys."""
seen, dups = {}, []
for c in cases:
# same target, same parameter names: probably only values changed
key = (c["target"], tuple(sorted(c["params"])))
if key in seen:
dups.append((seen[key], c["id"]))
else:
seen[key] = c["id"]
return dups

def may_implement(proposed, approved):
"""approved: {test_id: sha256 of the test's source}, frozen at human approval."""
if not approved:
return False, "a human has not approved the test list yet"
current = {c["id"]:...

Powered by Readybot.io

**DEV Community** ## Write the test plan before your coding …

DEV Community

Observal AI coding agent observability: sessions and traces

A developer tells you: « the agent ran for an hour, burned a pile of tokens and shipped nothing. » There is no error code and no stack trace, only a conversation. For that case, Observal AI coding agent observability means keeping the session itself, which is the ground Observal covers: an open source repository (Apache 2.0) showing about 4.3k stars on the GitHub page I read on 9 November 2026. This post approaches it through the operational decision it forces on you: what to keep from an agent session, where, and for whom. A note on method: span.observer did not deploy Observal. Everything below comes from the README, the repository documentation and the screenshots it publishes; anything I could not read is marked « unverified ». Observal AI coding agent observability: what is it? First trap: the name moved. The README now calls itself « Agentic Resource Registry » and states « Formerly Observal: same code, same maintainers, same Apache 2.0 license ». The command is still observal and old links redirect. Today the project describes itself as a self-hosted registry of resources for coding agents (skills, MCP servers, hooks, prompts, sandboxes) with a session-tracing layer on top. If you were looking for an LLM benchmark-style evaluation platform, that is not what the documentation describes (see limits below). Facts taken from the repository: Apache 2.0 licence, Python on the server and CLI (FastAPI, Typer), React for the UI, PostgreSQL 16 for the registry, ClickHouse for telemetry, Redis for the queue. The latest commit on the main branch is dated 7 November 2026 and pyproject.toml carries version 1.17.0-dev.0; I could not read a published release on the page, so I quote no stable number. The fork count (about 1.1k) comes from the same page. What it records from an agent session The mechanism is easy to state. A hook or extension in the harness (Claude Code, Codex, Cursor, Copilot, Kiro, OpenCode, Pi, Goose, Antigravity according to the README) reads the conversation transcript and sends it to the server. The CLI first writes to a durable local outbox, then posts to /api/v1/ingest/session; the server returns the highest acknowledged position, and delivery is described as « at-least-once with effectively-once storage ». observal reconcile replays what is missing from the local source. Each record becomes an event: prompt, response, tool call, result, token update, error, subagent. Per-session aggregates (tokens, models, tools, duration) feed the dashboards. The documentation is frank about a useful limit: detail depends on what the harness writes, and Observal does not intercept MCP traffic or invent fields. Interface tour: sessions, turns, calls The next screenshot comes from the repository (docs/img, covered by the project’s Apache 2.0 licence). It shows a Pi session: duration, 17 turns, input and output tokens, cache reads, tool call count, models, then the expandable list of turns with their duration. Source : github.com The session list gives the overview: platform, input and output tokens, tools called, duration, and a « Score » column that stays empty in this demo capture. It is the only evaluation hint I read there; I do not know when or how that score is filled. Source : github.com From complaint to diagnosis: the documented method The « Debug agent failures » guide walks through four moves, which I paraphrase: find the session (filters by harness, agent, model, period), read the events in order, inspect tool call detail, then patch delivery gaps with observal reconcile. Its pattern table is the most telling part: the same tool re-called with identical arguments points to a loop; a call with no result suggests an abrupt stop or an incomplete transcript; an expected MCP call that never shows up means the model did not pick it, or the harness did not record it. The commands in the capture below are those of the README and that guide. Installing Observal: server first, CLI second Per the README there are two parts: ...

Powered by Readybot.io

**DEV Community** ## Observal AI coding agent observability: sessions and …

DEV Community

Cache Repeated Answers to Cut LLM Calls in a Small-Business FAQ Bot (Python + SQLite)

Small businesses get the same ten questions all day: opening hours, delivery areas, how to book. Sending each one to a language model is wasted money. A small cache is one of the simplest ways to control AI automation costs for a small business. Normalise, then hash import hashlib, re, sqlite3, time

db = sqlite3.connect("answers.db")
db.execute("""CREATE TABLE IF NOT EXISTS cache(
key TEXT PRIMARY KEY, question TEXT, answer TEXT, created REAL, hits INTEGER DEFAULT 0)""")

def normalise(q: str) -> str:
q = q.lower()
q = re.sub(r"[^a-z0-9 ]+", " ", q)
return " ".join(q.split())

def key(q: str) -> str:
return hashlib.sha256(normalise(q).encode()).hexdigest()
Read through the cache MAX_AGE = 7 * 24 * 3600 # refresh weekly so answers do not go stale

def answer(q, call_model):
k = key(q)
row = db.execute("SELECT answer, created FROM cache WHERE key=?", (k,)).fetchone()
if row and time.time() - row[1] < MAX_AGE:
db.execute("UPDATE cache SET hits = hits + 1 WHERE key=?", (k,))
db.commit()
return row[0], "cache"
a = call_model(q)
db.execute("REPLACE INTO cache(key, question, answer, created) VALUES (?,?,?,?)",
(k, q, a, time.time()))
db.commit()
return a, "model"
Rules that keep it safe Cache only general questions. Never cache answers that include a customer's name, order or account details. Expire entries when prices, hours or policies change (clear the table on any website update). Log cache versus model so you can see what the cache actually saves. Going further Exact matching catches more than you expect once text is normalised. Fuzzy or embedding matching adds complexity; measure first. Why we often prefer small custom pieces like this over packaged bots: why small custom pieces often beat packaged bots. Disclosure: I co-founded NxFlowAI, an automation agency.

Powered by Readybot.io

**DEV Community** ## Cache Repeated Answers to Cut LLM Calls …

IGN India Pc

Xbox at 25: My Memories From Across Xbox’s Entire (and Quite Incredible) History

Jump in. The first day of my career was October 1, 2002. I’d been lucky enough to get hired by Official Xbox Magazine. The fledgling Xbox console was not quite a year old, and when I got settled in on the staff, it quickly became clear that Xbox was really about one …

Powered by Readybot.io

**IGN India Pc** ## Xbox at 25: My Memories From …

IGN India Pc

Xbox at 25: My Memories From Across Xbox’s Entire (and Quite Incredible) History

Xbox is officially 25 years old! Join IGN's Ryan McCaffrey as he looks back on a quarter-century of Microsoft's Xbox in the console market. We cover everything from the original Xbox and playing Halo: Combat Evolved, Splinter Cell, and KOTOR, to the glory days of the Xbox 360 versus the PS3. Hear his exclusive account of system-sellers like Gears of War, Mass Effect, and Oblivion, the reveal of the Xbox Series X, and discover how the upcoming Project Helix will compete with the PlayStation 6 (PS6) alongside future games like The Elder Scrolls 6: Sentinel.

Powered by Readybot.io

**IGN India Pc** ## Xbox at 25: My Memories From …

DEV Community

Identifiers,variables,Reserved words.

*Identifiers * A name in python program is known as identifier/variables or reference variables Rules for identifiers in Python The first character is a letter (a-z, A-Z) or an underscore _. The remaining characters are letters, digits 0-9 or underscores. It can’t be one of the reserved keywords Case matters: total and Total are consider as two different names. There’s no length limit. Name='gobi' //name is identifier
isStudent= True //isStudent is identifier
Rollno=12 //Rollno is identifier

can start with underscore() and double underscore(_) _name='gobi' //single underscore is also known as private
__rollno=13 //double underscore is also known as strictly private

Reserved Words or Keywords we can't use the keywords as identifier or variables. import keyword
print("The list of keywords are : ")
print(keyword.kwlist)

output:
The list of keywords are:
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

Powered by Readybot.io

**DEV Community** ## Identifiers,variables,Reserved words. *Identifiers * A name in …

DEV Community

Build a Call-to-Action Inventory: Extract Every Button and Link Label Across Your Site With Python

A homepage might say "Book a free call", the pricing page "Get started", the footer "Contact sales" and the blog "Talk to us". Four labels for one action. A free brand audit of your page copy will flag this on one page; this script shows it across the whole site. import re, urllib.request
from html.parser import HTMLParser
from collections import Counter, defaultdict

class CTA(HTMLParser):
def init(self):
super().init(); self.stack = []; self.found = []
def handle_starttag(self, tag, attrs):
a = dict(attrs)
if tag == "button" or (tag == "a" and re.search(r"btn|button|cta", a.get("class", ""))):
self.stack.append([tag, a.get("href", ""), ""])
def handle_data(self, d):
if self.stack: self.stack[-1][2] += d
def handle_endtag(self, tag):
if self.stack and self.stack[-1][0] == tag:
t, href, text = self.stack.pop()
text = " ".join(text.split())
if text: self.found.append((text, href))

def ctas(url):
html = urllib.request.urlopen(urllib.request.Request(url, headers={"User-Agent": "cta-inventory"})).read().decode("utf-8", "ignore")
p = CTA(); p.feed(html); return p.found

pages = ["https://example.com/", "https://example.com/services", "https://example.com/about"]
by_target = defaultdict(Counter)
for u in pages:
for text, href in ctas(u):
by_target[href or "(no href)"][text] += 1
for href, labels in by_target.items():
flag = " <- several labels" if len(labels) > 1 else ""
print(href, dict(labels), flag)
Reading the output One target, several labels: pick one label and use it everywhere. One label, several targets: "Get started" that sometimes opens a form and sometimes a pricing page confuses people. Vague labels: "Learn more" and "Click here" tell the user nothing. Limits This reads server HTML only. If your buttons render with JavaScript, extract the rendered DOM first. Class-name matching is a heuristic; adjust the regex to your theme. Combine the inventory with a tone check per page. The blunt single-page roast in the War Room gives a quick blunt read of a single page if you want a second opinion.

Powered by Readybot.io

**DEV Community** ## Build a Call-to-Action Inventory: Extract Every Button …

DEV Community

I Benchmarked Just My Socks on 3 Nodes. The Numbers Were Fine — My Test Method Wasn't

I Benchmarked Just My Socks on 3 Nodes. The Numbers Were Fine — My Test Method Wasn't I spent an evening benchmarking my Just My Socks subscription across three nodes, and the most interesting thing I found wasn't in the results table. It was that my first three test runs were measuring the wrong thing entirely — and nothing in the output told me so. Here's the data first, then the trap, because the trap is the part that will save you an evening. The results (evening peak, Nov 15, 2026) All three runs went through the proxy, verified by checking that the exit IP matched the node IP before trusting any number. Node Latency (client URL-Test) ChatGPT Claude Gemini s1 (Shadowsocks) 663ms 200, unlocked 302 to login page 200, unlocked s3 (VLESS) 199ms 200, unlocked 302 to login page 200, unlocked s4 (VLESS) 218ms 200, unlocked 302 to login page failed twice (timeout) A few things worth saying plainly about this table: ChatGPT was unlocked on all three nodes. No drama there. Claude returned 302 to the login page on every node — and that is not a failure. A 302 to /login is what an anonymous visitor gets. The failure mode to watch for is a hard 403, which is what the same test returned when I accidentally ran it on my direct connection. If Claude 403s you, your exit IP is flagged; if it 302s you to login, the IP is fine and you just need to sign in. Gemini is the one that actually varies by node. It worked on s1 and s3 and failed twice on s4. Same evening, same machine, same method. If Gemini is part of your workflow, node choice matters for it in a way it doesn't for ChatGPT. Latency is a snapshot, not a verdict. s1 measured 226ms in one run and 663ms in the next, minutes apart. s3 was 212ms then 199ms. Anyone showing you a single ping screenshot as proof a service is fast or slow is showing you weather, not climate. The full table, with method notes, lives on my guide's hands-on review page. I keep it updated with dated runs instead of rewriting history. The trap: my first three runs measured my direct connection Before the table above existed, I ran the benchmark script three times and got identical results on three different nodes. That should have been impossible — different exit IPs should at least change the latency numbers. It didn't, because the script wasn't going through the proxy at all. The script is Python, and Python's urllib on Windows ignores the http_proxy / https_proxy environment variables. It reads the proxy from the Windows registry (the system proxy setting) instead. My client sets up a local proxy on 127.0.0.1:10809, I set the env vars in PowerShell, ran the script — and it went direct every time. Three runs, three identical outputs, zero indication anything was wrong. The giveaway was switching to a completely different node — even a non-JMS one — and getting byte-identical results. Two things fixed it: Verify the exit IP first, then trust the test. One command before anything else: curl.exe -x http://127.0.0.1:10809 -s https://ifconfig.me/ip
If the IP it returns isn't your node's IP, your test traffic isn't going through the node and every number after it is fiction. Test through the proxy explicitly. curl -x forces the proxy regardless of what the runtime thinks the system proxy is. I also fixed the script itself to honor the env vars — the fix is in the repo — but the exit-IP check is the habit that matters, because it catches the problem no matter which tool you use. If you benchmark any proxy service on Windows with a Python script, do the exit-IP check first. The failure mode is silent: plausible-looking numbers, wrong network. How to reproduce this Pick a node in your client, then in PowerShell: curl.exe -x http://127.0.0.1:10809 -s https://ifconfig.me/ip
curl.exe -x http://127.0.0.1:10809 -s -o NUL -w 'ChatGPT %{http_code}\n' https://chatgpt.com/cdn-cgi/trace
curl.exe -x http://127.0.0.1:10809 -s -o NUL -w 'Claude %{http_code}\n' https://claude.ai
curl.exe -x http://127.0.0.1:10809 -s --max-tim...

Powered by Readybot.io

**DEV Community** ## I Benchmarked Just My Socks on 3 …

DEV Community

I Built 200+ Free Online Tools. Now Comes the Scary Part. 🚀

I can build websites. I can fix bugs. I can write code for hours. But can I actually build a business? That's the question that's been keeping me up at night. So I decided to find out. I built UsefulGo.com — a website with 200+ free online tools and games. And now comes the part that scares me more than any production bug: Getting real people to use something I built. Why I Started UsefulGo I'm a developer. Building things is what I love. But after spending so much time working on other people's products, I started wondering what it would feel like to build something of my own. Not just another GitHub repository. Not another unfinished side project. A real product. With real users. The idea behind UsefulGo is pretty simple. Every day, people need to compress PDFs, resize images, convert files, remove backgrounds, or solve dozens of other small digital problems. Why not put all those tools in one place? No complicated setup. No unnecessary steps. Just open the website, use the tool, and get on with your day. 200+ Tools Later... What started as a small experiment has grown into something I'm genuinely excited about. UsefulGo now includes: 📄 PDF tools — merge, split, compress, and convert files. 🖼️ Image tools — resize, compress, convert, and remove backgrounds. 🛠️ Everyday utilities for common digital tasks. 🎮 Browser games for when you need a little break. And I'm still adding more. The funny thing is, every time I finish building something, I come up with three more ideas. I guess that's what happens when you finally start building your own product. 😅 Coding Was the Easy Part Well... relatively easy. I thought the hardest part would be building the website. I was wrong. Now I'm learning about SEO, marketing, user acquisition, analytics, and all the things I used to think were someone else's job. Some days I'm incredibly excited about the possibilities. Other days, I'm staring at my analytics dashboard wondering how on earth people get their first 1,000 users. It's a strange feeling. You can spend hours building a feature, deploy it successfully, and feel amazing. Then you realize nobody knows it exists. A working product doesn't automatically become a successful product. That's probably the biggest lesson I've learned so far. I'm Not Trying to Build the Next Google I just want to build something useful. Something a student might bookmark. Something an office worker might use every week. Something that saves a stranger five minutes of frustration. And maybe, if I keep improving it, UsefulGo can eventually become a sustainable business. I don't know how long that will take. I don't even know if it'll work. But the thought of spending another year just thinking about starting something scares me more than the possibility of failing. So here I am. Building, learning, making mistakes, and figuring things out as I go. Can I Ask You a Small Favor? ❤️ If you have a minute, I'd really appreciate it if you checked out my project: 👉 https://usefulgo.com You don't need to sign up or buy anything. Just explore a little. And if something feels broken, confusing, or unnecessary, please tell me. I'd much rather hear honest criticism than spend months building features nobody needs. Also, if you're working on your own side project or startup, share it in the comments. I'd love to see what everyone else is building. Maybe we can support each other along the way. I have no idea where UsefulGo will be a year from now. But for the first time in a long time, I'm not just thinking about building my own business. I'm actually doing it. And that feels pretty damn good. 🚀 Thanks for reading. Back to building. ☕

Powered by Readybot.io

**DEV Community** ## I Built 200+ Free Online Tools. Now …

DEV Community

41% of cybersecurity job postings are remote: by specialty, country and pay (Nov 2026)

Remote security work is not rare. Of the 3,033 open cybersecurity roles at 696 employers that we read on 15 November 2026, 1,240 (41%) are remote, posted by 532 different employers. That is a lot of choice, but it is not spread evenly: some specialties are close to half remote, others closer to a third, and most remote roles are still tied to one country. Remote share by specialty Specialty Remote roles Of all its open roles Share remote Security architecture and consulting 191 388 49% Security operations and incident response 185 394 47% Penetration testing and red team 86 189 46% Application and product security 140 311 45% Security leadership 137 316 43% Cloud security 49 119 41% Threat intelligence and research 35 86 41% Identity and access management 52 148 35% Governance, risk and compliance 127 370 34% Security engineering 238 712 33% Two things stand out. Consulting and incident response are the most remote, close to half their roles. And security engineering, the largest specialty by far, is the least remote, at one role in three. Remote rarely means "from anywhere" Going by the location each posting gives, 35% of remote roles are remote within the United States, 12% within Europe, 7% within the UK and 6% within Canada. Only about 14% say "Remote" with no country or say anywhere, and some of those can still restrict who can be hired, which only the full posting shows. If you live outside the US, filter on where a role is open to, not on the word "remote". The board keeps a separate list of remote security roles open in any country or across several. Who hires remotely The employers with the most remote security roles open today: Palo Alto Networks (38), GuidePoint Security (36), CrowdStrike (30), Sophos (22), AHEAD (20), Zscaler (19), Huntress (13), Chainguard (13), Workstreet (12), Dragos (11), Coinbase (11) and JustMarkets (11). Security vendors and consultancies lead the list. Does remote pay less? A little. Among postings that state a range in US dollars, the middle of remote roles' ranges is $195k, against $207k for on-site and hybrid roles (560 and 794 ranges). A $12k gap is small next to the differences between specialties, where the middle runs from $167k for GRC to $246k for leadership (see what each specialty pays). Where this comes from CyberJobs.cc reads the hiring pages of security vendors, cloud and AI companies, banks, defense contractors and software firms every hour and keeps the security roles. A role counts as remote when its posting says so. Every remote role, with its location and stated pay, is on the remote cybersecurity jobs page, and the open dataset (CC BY 4.0) is on Hugging Face and Kaggle.

Powered by Readybot.io

**DEV Community** ## 41% of cybersecurity job postings are remote: …

DEV Community

Código HTTP 483: Evita que tus clientes congelen tu API

Código HTTP 483 – “I’m Not a Freezer”: Qué es, cuándo usarlo y cómo implementarlo hoy Introducción ¿Alguna vez tu API ha devuelto 429 Too Many Requests aunque el cliente no estaba enviando peticiones en exceso? En muchos casos el problema real es que el cliente está “congelando” recursos del servidor (manteniendo conexiones abiertas o dejando operaciones en espera) sin haberlo negociado. Para dar una respuesta clara y evitar malentendidos, el IETF está discutiendo el nuevo código 483 – “I’m Not a Freezer”. En este artículo práctico descubrirás qué significa este código, cuándo deberías devolverlo y, lo más importante, cómo implementarlo en tu stack sin romper nada. 1. ¿Qué indica el 483? Código Significado breve Cuándo usarlo 483 El servidor rechaza la petición porque el cliente intenta mantener recursos en estado de espera sin autorización. Cuando el cliente mantiene una conexión, una transacción o un lock más allá del tiempo acordado y el servidor decide cerrarlo. 429 Rate‑limit excedido. Cuando la velocidad de peticiones supera el límite permitido. 503 Servicio temporalmente no disponible. Cuando el servidor está sobrecargado o en mantenimiento, sin relación con la conducta del cliente. En resumen, 483 habla de persistencia no autorizada, mientras que 429 y 503 se centran en cantidad y disponibilidad respectivamente. 2. ¿Por qué surge ahora? Micro‑servicios y APIs modernas manejan recursos escasos (sockets, hilos, buffers). Los clientes a menudo usan long‑polling o WebSocket sin acordar límites de tiempo, lo que bloquea recursos críticos. La especificación actual no tiene un código que exprese explícitamente “no voy a mantener este recurso para ti”. El 483 cubre ese vacío y permite a los equipos de operaciones comunicar la razón del rechazo de forma inequívoca. 3. Ejemplos concretos 3.1. Node.js / Express app.post('/jobs/:id/start', (req, res) => {
const job = jobs.get(req.params.id);

// El cliente intenta re‑usar un job que ya está en estado "running"
if (job && job.status === 'running' && !req.headers['x-keep-alive']) {
// 483 + cuerpo JSON explicativo
return res.status(483).json({
error: "I’m Not a Freezer",
message: "El cliente está intentando mantener el job activo sin autorización."
});
}

// lógica normal…
});
3.2. Nginx como proxy (respuesta estática) # Bloquear peticiones que superen 30 s sin header X-Keep-Alive
map $http_x_keep_alive $freeze {
default 0;
"" 1;
}
if ($request_time > 30 && $freeze) {
return 483 "I’m Not a Freezer\r\n";
}
3.3. cURL (para probar) curl -i -X POST http://api.ejemplo.com/jobs/42/start \
- H "Content-Type: application/json" \
- d '{"payload":"…"}'
Si el servidor devuelve: HTTP/1.1 483 I’m Not a Freezer
Content-Type: application/json
...
{
"error":"I’m Not a Freezer",
"message":"El cliente está intentando mantener el job activo sin autorización."
}
…has recibido la señal correcta. 4. Guía de adopción paso a paso Identifica los puntos críticos donde tu API permite “esperas largas” (polling, streaming, jobs). Define un header de negociación, por ejemplo X-Keep-Alive: true o Prefer: wait=30. Implementa la lógica de rechazo con el código 483 cuando falte el header o el tiempo supere el límite. Documenta el código en tu OpenAPI/Swagger: responses:
'483':
description: I’m Not a Freezer – el cliente intentó mantener recursos sin autorización
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
Actualiza los clientes para que manejen 483 como una condición recuperable: volver a enviar la petición con la negociación adecuada o abortar la operación. Monitorea la métrica http_response_status{code="483"} en tu sistema de observabilidad para detectar patrones de abuso. 5. Ventajas y desventajas Pros Contras Comunicación clara del motivo del rechazo. Necesita coordinación entre cliente y servidor (header de negociación). Evita que los clientes “bloqueen” r...

Powered by Readybot.io

**DEV Community** ## Código HTTP 483: Evita que tus clientes …

DEV Community

Ada for Beginners: The Language That Keeps Airplanes Flying and Rockets Safe

Ada is a programming language built for systems where a bug is not just an annoyance — it is a disaster. It powers air traffic control, railway signalling, satellites, and military systems, because it was designed from the start to catch mistakes before the program ever runs. If you have only used Python or JavaScript, Ada will feel strict. That strictness is the whole point. Ada was commissioned by the US Department of Defense in the late 1970s and named after Ada Lovelace, the first programmer. The language enforces strong typing, which means you cannot accidentally mix up a temperature and a distance, or a speed and a weight. The compiler refuses to build your program until the types line up. That sounds annoying, but in a flight-control system it is exactly what you want. A tiny Ada program looks like this: with Ada.Text_IO; use Ada.Text_IO;

procedure Hello is
begin
Put_Line ("Hello, Ada!");
end Hello;
Every program is built from packages, procedures, and tasks. Packages group related code together. Tasks are Ada's built-in way to run things at the same time — no external library needed. That matters, because concurrency bugs are some of the hardest bugs to find, and Ada's tasking model was designed to make them easier to reason about. Ada is also famous for contracts. You can write down what a function expects and what it promises to return, and the compiler or the SPARK tools can check those promises. SPARK is a subset of Ada used when you need mathematical proof that the code is correct. It is used in train control systems in Europe and in medical devices where failure is not an option. Who is Ada for? Engineers building safety-critical software, students who want to understand what strong typing really buys you, and anyone curious about how the most reliable software in the world is written. It is not the language you pick for a weekend side project or a startup landing page — the ecosystem is small, libraries are fewer, and hiring Ada developers is harder than hiring JavaScript developers. The limits are honest. Ada's tooling is good but not as polished as Rust or Go. The community is small, so finding answers takes longer. And most companies will never need Ada's level of assurance. But if you learn Ada, you will write safer code in every other language too, because it teaches you to think about types, contracts, and failure modes before you write a single line. Start today with one step: install GNAT Community Edition, write the Hello program above, then change the type of a variable on purpose and watch the compiler refuse. That refusal is Ada doing its job — and it is the best first lesson the language can teach you.

Powered by Readybot.io

**DEV Community** ## Ada for Beginners: The Language That Keeps …

DEV Community

How I Turned My Idle RTX 4060 into the Backend for a Free Audio Separation Website

I had an RTX 4060 in my Windows PC that wasn’t getting much gaming time. Instead of leaving it idle, I connected it to a website. The result is UVR Online: users upload a song, choose a separation model, then listen to and download the separated tracks. The website runs in the cloud. The actual audio inference runs on my local computer in China, serving users around the world. I didn’t need a public IP address at home or port forwarding. The architecture Browser ── upload ──► Private Cloudflare R2
│ ▲
│ │ download input / upload results
▼ │
Next.js → Go API ◄──── Windows Python Worker
│ └── PyTorch CUDA → RTX 4060
├── PostgreSQL
└── Redis
Each part has a specific job: Next.js provides the interface, task pages and audio players. Go handles task creation, permissions, upload authorization and Worker coordination. PostgreSQL stores the authoritative task state. Redis handles rate limiting. Cloudflare R2 holds private input and output files. Python, PyTorch CUDA and FFmpeg handle audio validation, separation and encoding locally. The separation engine builds on the UVR ecosystem and headless inference code. I’m building the web service around existing models, rather than training a new separation model. The local PC pulls work The cloud server never opens a connection to my home computer. Instead, the Worker makes outbound HTTPS requests to register, send heartbeats and claim tasks. For each task, it: Downloads the input using a short-lived signed URL. Checks the actual audio with ffprobe. Runs the selected model on the GPU. Encodes and uploads the results to R2. Reports completion to the API. The browser also uploads directly to R2. This keeps large audio files out of the application server’s data path. Private results are accessed through short-lived signed URLs, rather than public bucket links. A home computer needs recoverable tasks A local PC can restart. The network can disconnect. A process can crash halfway through a song. To handle that, claimed tasks have a lease and an attempt token. The Worker renews the lease while it works. If the lease expires, the server can requeue the task within its retry limit. An old attempt cannot later overwrite the result of a newer attempt. Claiming is atomic in PostgreSQL, and completion is idempotent. Redis is useful for rate limiting, but it doesn’t become a second source of truth for task status. This provides recovery, not uninterrupted availability. A single local machine is still a single point of failure. The surprising bottleneck was file transfer My first assumption was that GPU inference would dominate the wait. In practice, moving audio between my local PC and overseas storage became an important bottleneck—especially over a slow VPN route. Output size matters too. A small MP3 can expand into much larger uncompressed tracks. For example, five minutes of 44.1 kHz, 16-bit stereo audio is roughly 50.5 MiB per WAV track, before headers. Two stems are about 101 MiB. That is why export format is an infrastructure decision as well as a user preference. I also changed the Worker from a fully serial workflow to a bounded pipeline: Task A: upload results
Task B: run inference
Task C: download input
Only one task uses the GPU at a time. Network transfers can overlap with inference, and temporary disk usage is bounded. This reduces avoidable GPU idle time. It doesn’t make the internet connection faster, and a backed-up upload stage still applies backpressure. Free processing still needs limits Removing a daily usage cap doesn’t remove the need for admission control. Free processing is constrained by file size, duration, supported formats and queue capacity. Each free user can have only one unfinished task. Network-level limits and bot verification add another layer of protection. The point is to let someone process another song after finishing t...

Powered by Readybot.io

**DEV Community** ## How I Turned My Idle RTX 4060 …

DEV Community

COBOL for Beginners: The 60-Year-Old Language That Still Runs the World's Money

COBOL is older than the internet, older than the personal computer, and it still moves trillions of dollars every single day. About 80% of in-person financial transactions touch COBOL somewhere — ATM withdrawals, card payments, bank transfers, insurance claims. If you think old languages are dead, COBOL is the counterexample that will change your mind. COBOL was created in 1959 to be readable by non-programmers. Its code reads almost like English, which is why a COBOL program looks nothing like Python or Java. A division structure organizes everything: IDENTIFICATION DIVISION names the program, ENVIRONMENT DIVISION describes the machine, DATA DIVISION declares every variable, and PROCEDURE DIVISION does the work. Here is a tiny COBOL program: IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO-WORLD.
PROCEDURE DIVISION.
DISPLAY "Hello, COBOL!".
STOP RUN.
That verbosity is a feature, not a bug. In 1959, the goal was that a business manager could read the code and understand what it did. Sixty years later, banks still value that readability, because their core systems are millions of lines of COBOL that nobody wants to rewrite — and nobody fully understands anymore. Why does COBOL survive? Because rewriting a bank's core system is one of the riskiest projects in software. The code works, it has been tested by decades of real transactions, and a replacement could introduce bugs that cost billions. So instead of replacing COBOL, banks keep it running on mainframes, and they hire people to maintain it. COBOL developers are in short supply, which means the pay is often surprisingly good. COBOL is not for everyone. The tooling feels ancient, the community is small, and you will not build a startup with it. The language has no modern package ecosystem, and writing a web API in COBOL is an exercise in patience. But if you want a stable, well-paid niche where your skills are genuinely scarce, COBOL is one of the best-kept secrets in programming. Start today with one step: install GnuCOBOL, write the Hello program above, then write a program that reads a list of numbers and adds them up — the kind of batch job COBOL was born to do. You will understand in an hour why banks still trust it.

Powered by Readybot.io

**DEV Community** ## COBOL for Beginners: The 60-Year-Old Language That …