Pixel

Pixel Updates & Events

Server Updates & Events

Announcements

DEV Community

WARN Act and Layoffs in Pharmaceuticals and Biotech — What Drug and Biotech Workers Should Know

Drug and biotech companies rarely announce layoffs the way a factory closure does. The cuts arrive as pipeline pivots, post-acquisition integrations, and cash-runway restructurings — and when they are big enough, they show up as WARN notices. Five recent pharmaceutical and biotech filings in the Layoff Atlas registry cover 526 workers, from an 88-person research institute in Los Angeles to a 205-person generic-drug operation in New York. These are reported plans, not confirmed job losses — filings can be amended, delayed, or cancelled before the effective date. The recent filings The five filings span California, New York, New Jersey, and Maryland: Ellison Medical Institute, Los Angeles, California — 88 workers. The cancer research and biotechnology institute filed notice on November 4, 2026, for 88 permanent job cuts effective January 3, 2027, at its Exposition Boulevard campus — exactly 60 days' notice. Crinetics Pharmaceuticals, San Diego, California — 78 workers. The endocrine drug developer's cuts take effect January 4, 2027, following Vertex Pharmaceuticals' $10 billion acquisition, which closed September 1, 2026. The registry listing originally carried a mistyped 2026 effective date; trade reports confirm January 4, 2027. InvaGen Pharmaceuticals, Long Island, New York — 205 workers. Two sites: 160 workers in Central Islip and 45 in Hauppauge, both noticed August 28, 2026, and effective November 27, 2026 — 91 days' notice, both permanent layoffs. Teva Pharmaceuticals USA, Fairfield, New Jersey — 61 workers. Effective February 1, 2027, filed with the New Jersey Department of Labor and Workforce Development. The notice date is unavailable in the record, so the notice period cannot be checked from the filing alone. Pharmaceutics International, Hunt Valley, Maryland — 94 workers. Notice published October 30, 2026, effective December 29, 2026 — exactly 60 days. The company is listed as a Jabil Inc. company. A sixth filing shows the other end of the scale: DesigneRx Pharmaceuticals in Vacaville, California, filed a 7-worker notice effective November 15, 2026 — far below the federal thresholds, a reminder the registry records what employers file, not only qualifying mass layoffs. Why pharma layoffs look different Pharmaceutical layoffs follow the drug-development cycle more than the business cycle. Three patterns show up again and again: Post-acquisition integration. Crinetics is the clearest recent example: Vertex completed a $10 billion acquisition on September 1, 2026, and the 78 San Diego cuts followed as the two companies combined operations. Acquired companies' corporate functions — finance, IT, and administrative roles — are usually the first to go. Pipeline pivots. When a trial fails or a program is deprioritised, the team behind it is cut. Clinical-stage biotechs run on cash runway, and a restructuring that extends that runway by a few quarters is often the stated reason. Commercial restructuring. Larger drugmakers cut sales forces when a product line is reorganised. Industry trackers counted 90 biopharma workforce-reduction events in 2026, covering more than 27,446 roles — mostly global announcements, not US WARN filings. The notice period depends on the state The federal WARN Act generally requires employers with 100 or more full-time employees to give 60 days' notice of a qualifying mass layoff or plant closing. But the states where pharma concentrates add their own rules, and the recent filings show all three in action: California requires 60 days' notice from employers with 75 or more employees — a lower threshold than the federal law. Ellison's 60 days and Crinetics' roughly 62 days both clear it. New York requires 90 days' notice from employers with 50 or more employees. InvaGen's 91 days clears that bar by a single day. New Jersey requires 90 days' notice, and its law also mandates severance in covered layoffs. Teva's missing notice date means the timing cannot be verified from the public record — a gap wor...

Powered by Readybot.io

**DEV Community** ## WARN Act and Layoffs in Pharmaceuticals and …

DEV Community

Writing a comparison page that says 'not affiliated': honest competitor mentions for SEO

I needed a page that would rank for a search term people actually type: the name of a big, well-known downloader. My own site is small and new, and nobody searches for its name yet. People do search for the big names, though, and for alternatives to them. So I built a comparison page that names the competitor, explains what my tool does differently, and carries the words "not affiliated" in plain sight. The idea sounds like a trick. It should not be one. The page only works if it is honest, because a comparison page that lies about a competitor is the fastest way to lose a visitor's trust. Here is how I approached it. Name the competitor, then stop selling The first decision is which name to target. I picked the biggest tool in my niche, the one whose name people type when they want to download a TikTok video. The page title and H1 carry that name next to the word "alternative", which is what people search for when the big tool is down, slow, or full of pop-ups. Then I made a rule for myself: the page may mention the competitor, but it may not claim anything about them I cannot verify. No "they sell your data". No "they are unsafe". No invented weakness. If I cannot prove it, I do not write it. What I can say, truthfully, is what my tool does: no sign-up, no watermark games, the formats labelled honestly. That is the comparison. My tool against a standard, not my tool against a straw man. The "not affiliated" line is not decoration Every comparison page I wrote carries a short line near the top: we are not affiliated with, endorsed by, or connected to the named service. I put it there for two reasons. The first is legal common sense. Using another brand's name to describe what your own product does is nominative use — the same way a repair shop can say "we fix iPhones" — but that only holds while you are genuinely describing, not pretending to be them. The disclaimer makes the relationship clear to a visitor in one glance. The second reason is trust. A visitor who lands on a page that names a famous tool wants to know immediately whether they are on the real site or a copy. The honest answer — no, this is a different tool, here is what it does — is the only answer that keeps them reading. If they feel tricked, they leave. The table has to be true The middle of the page is a simple comparison table: feature, them, us. I keep the rows to things I can check in a minute. Does it need an account? Does it work on a phone browser? Does it label audio honestly instead of promising an MP3 that does not exist? Each row is a fact, not an opinion, and I leave out any row where I would have to guess. I also wrote the limits of my own tool into the same page. If a format tops out at 360p with sound, the page says so. If a platform's links sometimes fail, the page says that too. That sounds like bad marketing, but it is the reason visitors stay: the page reads like it was written by someone who actually built the thing, because it was. Why this helps search at all Search engines do not reward the word "alternative" on its own. They reward pages that answer the question behind the search. Someone typing a big brand's name plus "alternative" is asking: is there something simpler, and can I trust it? A page that names the brand honestly, disclaims the connection, shows a true table, and admits its own limits answers that question in full. That is the whole technique. There is no cleverness to it, which is the point. A comparison page is a trust exercise dressed up as SEO, and the SEO only works if the trust part is real. I built the page on ramrom.io, where the TikTok downloader it describes actually runs, so anyone reading it can try the tool in the same visit and judge for themselves whether the comparison was fair. That is the test I keep coming back to: if the page cannot survive the visitor trying the thing, the page is not finished yet.

Powered by Readybot.io

**DEV Community** ## Writing a comparison page that says 'not …

DEV Community

How to verify a J-cut or L-cut in an exported MP4

Someone asks for a small revision to an interview. “Let me hear the answer, but stay on the listener for another half second.” The new MP4 sounds plausible. How do you tell whether the editor held the picture or slid the entire voice track? We made three 5.75-second reference exports at Valmera from the same exchange in the Sintel trailer. You can download all three and run the checks below. The reference renders use FFmpeg. The dialogue is the control: every version has the same decoded audio. File Audio edit Picture edit Straight cut 2.625 s 2.625 s J-cut 2.625 s 3.250 s L-cut 2.625 s 2.000 s Listen to the first word of the answer The line is “I'm searching for someone.” The audio edit occurs at 2.625 seconds. The first word comes around 2.8 seconds. That gap matters in an editing brief. “Hold for half a second after the first word” and “hold for half a second after you change audio takes” give different picture edits. Pause each file at 2.917 seconds. The straight cut has moved to a wide view. The J-cut still shows the close-up while the answer is underway. Now step back to 2.125 seconds. The L-cut shows the dark opening of the incoming shot while the question is still audible. That darkness is present in the source trailer. Compare the sound in the files If you control the export, encode the approved audio once and mux it into each picture version. Decode the audio to the same PCM format before comparing the files: for cut in straight j-cut l-cut; do
printf '%s ' "$cut"
ffmpeg -v error -i "$cut.mp4" -map 0:a:0 \
- c:a pcm_s16le -f hash -hash sha256 -
done
All three hashes match. The MP4 file hashes differ because their pictures differ. If an editor re-encodes audio for each export, a different PCM hash on its own does not tell you whether speech moved. Compare the timing of a short waveform passage before the join and again after it. Our source check compares seven 100-ms windows, including one on either side of the audio edit. See the measurements and exact file hashes. Check the picture against the source clock At 24 fps the straight cut switches takes at output frame 63, counting the first frame as zero. The J-cut switches at frame 78. The L-cut switches at frame 48. When the second take is visible, its source frame is the output frame plus 90. At output frame 70 the straight cut therefore shows source frame 160, while the J-cut still shows source frame 70. You can extract that moment without relying on a player's rounded time display: ffmpeg -v error -i j-cut.mp4 -vf "select=eq(n\,70)" -frames:v 1 frame-70.png
For your own interview, check one later word where the new speaker's mouth is visible. An editor can place the first transition correctly and still slip the rest of the sentence. Download the source, rebuild script and trial brief. For a separate example produced through Valmera, the four-second B-roll edit changes the picture on the word “green” while the presenter keeps speaking. Watch the recorded edit. Footage from Sintel, © Blender Foundation, CC BY 3.0. Valmera trimmed, resized and rearranged the excerpt and extracted the comparison frames. This tutorial was prepared with AI assistance.

Powered by Readybot.io

**DEV Community** ## How to verify a J-cut or L-cut …

DEV Community

Fusion splice loss: what the 0.03 dB on the splicer screen actually means

Fusion splice loss: what the 0.03 dB on the splicer screen actually means Part 8 of the series "Data center field notes" — field notes from real break-fix work. The splicer finishes the arc, the screen shows 0.03 dB, and the instinct is to call it good and move on. That number is not a measurement. It is the splicer's estimate of the loss, judged from the fiber image: how well the cores aligned, how clean the cleaves were. The real measurement comes later, on the OTDR trace or the span test. The estimate is still useful, but only if you read it the right way: against the loss budget for that span, not against a number you memorized, and with a clear rule for when to cut the splice and do it again. What the splicer actually estimates A fusion splicer does not measure light passing through the joint. It looks at the two fiber ends — core alignment, cleave angle, any chips or dirt on the end-face — and estimates the loss that joint should cause. A clean, square cleave with the cores dead-on gives a low estimate; a chipped cleave or a slight offset gives a higher one. That is why the estimate can change when you re-splice the same two fibers: nothing about the fiber changed, only the quality of the ends you presented to the arc. Treat the number as a screen at the tray, not as the record. The number that goes in the ticket as proof is the OTDR loss step or the span test, measured after the splice is protected and the tray is closed. This is the read I use after 200+ splices — single-mode and multimode, stripped, cleaned, and cleaved by hand, fused, protected in trays and enclosures, and validated with Fluke testing and OTDR and VFL checks. Read it against the budget, not against a universal number There is no pass/fail number for a single splice. The span's loss budget is the pass/fail line: add up the fiber attenuation for the distance, the connector losses, and every splice on the span, and compare the total to the budget. A 0.08 dB splice can be fine on a short span with budget to spare and a fail on a long span that is already near its limit. The same estimate means different things on different jobs, which is why a tech who memorizes "0.05 is good, 0.10 is bad" will pass splices that fail the span and re-do splices that were fine. Before you start splicing, know the budget for the span and how much of it is already spoken for by the other splices and connectors. Then each estimate gets judged against what is left. The re-splice decision: cleave first, always When the estimate comes back high against the budget, the first suspect is the cleave, not the splicer. Cut the splice, and inspect both ends before you touch anything else: cleave angle, chips on the edge, dirt on the end-face. Then re-strip, clean, and cleave both fibers and re-fuse. In most cases the second estimate drops sharply, because the first cleave was the problem. If the estimate stays high after a clean re-cleave, stop burning splices on the same joint and widen the check: are the two fibers the same type — single-mode to single-mode, multimode to multimode, same core size? Is the alignment actually seating, or is the fiber slipping in the holder? Are the electrodes worn, giving a weak or uneven arc? A high estimate that survives a good re-cleave is the splicer telling you something about the fiber or the machine, and re-fusing the same ends a third time will not fix it. Single-mode vs multimode: what "good" looks like The estimate behaves differently by fiber type, and so does the proof. On single-mode, the core is 9 microns and alignment is everything — a small offset is a real loss, so the estimate is sensitive and the OTDR will show a clean loss step at the splice point. On multimode, the core is 50 or 62.5 microns and the splice is more forgiving of alignment, but the estimate is also less precise, and the OTDR trace can show a gainer — a step that looks like gain rather than loss — depending on which direction you shoot the trace. That is why a multi...

Powered by Readybot.io

**DEV Community** ## Fusion splice loss: what the 0.03 dB …

DEV Community

Want to build a browser extension? If you know JavaScript, you’re already most of the way there.

Browser extensions are one of the most underrated things a web developer can build. They’re small, useful, and you can ship one fast. Here’s what helped me understand them. What an extension is made of: manifest.json: The config file. It defines the name, version, permissions, and which scripts run where (use Manifest V3 for new projects) Content scripts: JavaScript that runs inside web pages, so you can read or change the page Background service worker: Handles events and long-running logic behind the scenes Popup / options page: The UI users see. It’s just HTML, CSS, and JS, and you can build it with React Storage and messaging APIs: Save user settings and let the different parts talk to each other Tips for getting started: Start with a tiny idea, like a button that changes a page’s style or saves a note Ask only for the permissions you really need. Users and store reviewers care about this Load your extension unpacked in developer mode to test changes quickly Use a bundler like Vite if you want React or TypeScript in your popup Chrome, Edge, and Firefox share most of the WebExtensions API, so one codebase can often cover several browsers Common mistakes: Requesting broad permissions “just in case” Forgetting that service workers in Manifest V3 can shut down, so don’t keep state only in memory Skipping a clear privacy explanation before publishing The best part is that you can solve a real problem you have every day, and publish it for others to use. Have you built a browser extension, or do you have an idea for one? BrowserExtensions #JavaScript #WebDevelopment #Chrome #React

Powered by Readybot.io

**DEV Community** ## Want to build a browser extension? If …

DEV Community

Steganography for Developers: Hiding Data in Plain Sight

Most developers know cryptography. You encrypt a message, and anyone who intercepts it sees gibberish. But gibberish itself is a signal: it tells an observer that something secret is going on. Steganography takes the opposite approach. Instead of making a message unreadable, it makes the message invisible. The word comes from the Greek steganos ("covered") and graphein ("writing"). The goal is that an observer never suspects a hidden message exists at all. In this article we'll look at how it works, build a working image steganography tool in Python, and discuss where it is (and isn't) useful. Steganography vs. Cryptography Cryptography Steganography Hides The content of a message The existence of a message Visible to an observer? Yes, as ciphertext No, looks like a normal file Failure mode Message stays unreadable Message is discovered They are not competitors. The strongest setups combine them: encrypt first, then hide the ciphertext. If the stego layer is ever detected, the attacker still faces encryption. A Short History Steganography is much older than computers. Herodotus describes messages tattooed on a shaved head and covered by regrown hair. Invisible inks made from lemon juice or milk were used for centuries. In WWII, spies used microdots, entire pages photographed down to the size of a period. Digital steganography follows the same principle: find a place where a small change goes unnoticed. The Core Idea: Redundancy Digital media is full of redundancy. A photo contains far more detail than the human eye can perceive, and an audio file contains frequencies we can't hear. Steganography replaces some of this "unimportant" data with secret data. The classic technique is LSB (Least Significant Bit) substitution. Each pixel in an RGB image has three channels, each stored as a byte (0–255). Consider a red value of 200: 200 = 11001000
If we flip the last bit, we get 11001001, which is 201. Nobody can see the difference between red 200 and red 201. So every channel can carry 1 hidden bit, and every pixel carries 3 bits. A 1920×1080 image has about 2 million pixels, which gives roughly 6.2 million bits, or about 777 KB of hidden capacity. That's plenty for text. Building It in Python Let's implement an LSB encoder and decoder with Pillow. pip install pillow
Step 1: Convert text to bits We need to know where the message ends. A simple approach is to prefix the payload with its length (32 bits). def to_bits(data: bytes) -> list[int]:
length = len(data)
header = [(length >> i) & 1 for i in range(31, -1, -1)]
body = [(byte >> i) & 1 for byte in data for i in range(7, -1, -1)]
return header + body

def from_bits(bits: list[int]) -> bytes:
length = 0
for bit in bits[:32]:
length = (length << 1) | bit

payload = bits[32:32 + length * 8]
out = bytearray()
for i in range(0, len(payload), 8):
    byte = 0
    for bit in payload[i:i + 8]:
        byte = (byte << 1) | bit
    out.append(byte)
return bytes(out)

Step 2: Embed the bits from PIL import Image

def embed(cover_path: str, out_path: str, message: str) -> None:
img = Image.open(cover_path).convert("RGB")
pixels = list(img.getdata())

bits = to_bits(message.encode("utf-8"))
capacity = len(pixels) * 3
if len(bits) > capacity:
    raise ValueError(
        f"Message too large: need {len(bits)} bits, have {capacity}"
    )

new_pixels = []
bit_index = 0
for r, g, b in pixels:
    channels = [r, g, b]
    for c in range(3):
        if bit_index < len(bits):
            channels[c] = (channels[c] & ~1) | bits[bit_index]
            bit_index += 1
    new_pixels.append(tuple(channels))

stego = Image.new("RGB", img.size)
stego.putdata(new_pixels)
stego.save(out_path, format="PNG")  # must be lossless!

Note the output format: PNG. This is critical, and we'll come back to it below. Step 3: Extract the bits def extract(stego_path: str) -...

Powered by Readybot.io

**DEV Community** ## Steganography for Developers: Hiding Data in Plain …

DEV Community

Spec tables on product pages: keep them as real tables, and make them work on a phone

Every hardware or electronics product page ends up with a spec table. Thread size, length, material, voltage range, what's in the box. It's the part of the page people scroll straight to when they already know what they want. It's also the part that gets built as a pile of

s, or a two-column grid that falls apart below 400px. A few notes on doing it in a way that holds up. Use a table. It is tabular data. Key/value specs are exactly what is for. You get row and header relationships for free, and screen readers can announce "Material, stainless steel" instead of two unrelated words.
Specifications

Material Stainless steel A2 Thread M8 Length 60 mm


The two details people skip: scope="row" on the label cell, and a . The caption can be visually hidden if the section already has a heading, but having it means the table has a name when someone jumps between tables. A description list ( ) is also a fair choice for a short, simple list of specs. Once you have more than a handful of rows, or anything that compares variants, go with the table. Comparing variants is where it gets harder When one product comes in several sizes, the spec table often turns into a grid: sizes across the top, properties down the side.
Sizes

6 mm 8 mm 10 mm

Drill bit 6 mm8 mm10 mm Screw size 4–5 mm5–6 mm6–8 mm


(The values here are just example data to show the shape.) With both scope="col" and scope="row", a screen reader can announce the cell with both headers. Without them, someone tabbing through hears a list of numbers with no context. Making it fit on a phone There are two honest options. Pick based on the table's shape. Two-column key/value tables: let them wrap. Most of the time the only CSS you need is to stop the label column from being squeezed: .specs { width: 100%; border-collapse: collapse; }
.specs th, .specs td { padding: .5rem .75rem; text-align: start; vertical-align: top; }
.specs th { width: 40%; font-weight: 600; }
.specs tr + tr > * { border-top: 1px solid #e5e7eb; }
Note text-align: start instead of left. If the site also runs in a right-to-left language, it flips on its own. Wide comparison tables: let them scroll horizontally inside a container, and don't fight it.



Sizes ...


.table-scroll { overflow-x: auto; }
.specs--compare th[scope="row"] {
position: sticky;
inset-inline-start: 0;
background: #fff;
}
The tabindex="0" and role="region" with a label let keyboard users focus the scroll area and move it with the arrow keys. The sticky first column keeps the row labels visible while the values scroll. What I'd avoid is the "turn every row into a card with display: block" trick. It looks neat, but changing a table's display can strip its table semantics in some browser and screen reader combinations, and then you've lost the thing that made it a table in the first place. If you go that route, test it with an actual screen reader. Small things that make specs easier to read Units in the value, consistently. "60 mm" in every row, not "60" in one row and "6 cm" in the next. If you show two unit systems, pick one as primary and keep the other in a smaller span. Non-breaking space between number and u...

Powered by Readybot.io

**DEV Community** ## Spec tables on product pages: keep them …

DEV Community

Turning an Old Phone Into an AI Lens: Real-Time Reality-to-Otherworld Rendering

The build in this episode is a hardware one: an old phone becomes a camera that shows the world through an image model instead of through glass. Every frame you point it at comes back restyled. The interesting part is not the phone. It is what MOSS Image has to do to make that loop feel live, and where the illusion breaks. The five workflows # Workflow Role in the loop 01 Colour-depth LoRA Light and colour control on the restyle 02 50-step vs 8-step DMD vs 4-step Turbo Picks the operating point 03 Base vs SFT, same parameters Picks the model variant 04 JSON Layout structured composition Keeps the scene structurally readable 05 SFT 50-step high quality The quality ceiling, used for stills The step-count decision is the whole design Live-style rendering has a latency budget measured in single-digit seconds. That rules out 50 steps immediately. Between 8-step DMD and 4-step Turbo, the trade is: 4-step Turbo — fast enough that pointing the phone feels responsive. Detail is soft; fine structure (text, railings, thin branches) dissolves. 8-step DMD — noticeably richer, but the added latency makes the phone feel like a camera with a delay, not a lens. The episode picks Turbo for live use and switches to 8-step for “pause and capture” moments. That split is the actual engineering answer. Where the illusion breaks Three things break it, in order of severity: Motion. Fast panning produces smeared frames because the model re-imagines each frame independently. There is no temporal coherence between frames. Text. Any signage in view gets re-lettered into nonsense within a second or two. Faces. Strangers in the background become stylised, which reads as uncanny rather than magical. Knowing that list is more useful than any demo reel: it tells you what to point the thing at. What this is actually good for Location scouting with a stylised preview — you see composition and lighting relationships quickly. Live performance visuals where latency is tolerable. Teaching: showing people the difference between “what the model sees” and “what is there” is a better demo than a static image. Files HF: https://huggingface.co/AI-KSK/moss-image-five-workflows ModelScope: https://modelscope.cn/models/Sunjian520/moss-image-five-workflows Bilibili (Chinese original): https://www.bilibili.com/video/BV1rTnN6vE7A/ Notes The hardware build is described in the video; the repository contains only the ComfyUI workflows. Tutorial material, not a product. Independent AI-KSK release; not affiliated with the model authors.

Powered by Readybot.io

**DEV Community** ## Turning an Old Phone Into an AI …

DEV Community

Moving a browser game's controls into the arena

A timing game can have simple rules and still make the player look in too many places. That was the problem I worked through while building SlapLoop, a free cartoon browser game against the CPU. The early layout put the arena in one area and the strategy controls, power meter, and a large action button in another. It was understandable as a web page, but it split the player's attention at the exact moment the game needed it. The useful change was to treat the arena as the input surface and make the character's movement communicate the same timing information as the meter. One place to look, one place to act The current interaction is deliberately small: click the arena for an instant slap, or hold and release to time the swing. A short hint explains both actions inside the game, while a separate controls guide provides the detail. Character selection lives in arena settings. Tactics and the once-per-match skill remain close to the action. This matters because the player is making two different kinds of decisions: a slower choice about approach, and a quick choice about release timing. They need different emphasis even when they share the same screen. Removing the large action button also created a responsibility: the clickable surface had to be discoverable. A canvas that responds to input without a visible hint is not automatically more immersive. The hint is part of the interaction, rather than an instruction paragraph competing with the match. Animation should describe the timing state A moving meter is a precise indicator, but the hand movement gives the action a physical meaning. If they disagree, the player has to decide which one to trust. A useful way to think about the renderer is as a consumer of the same normalized power value used by the game. The following is a conceptual sketch, not a copy of the game's source: function timingView(power) {
const bounded = Math.max(0, Math.min(1, power));
return {
meterPosition: bounded,
windupAngle: -0.4 + bounded * 1.1,
};
}
The exact angles depend on the character art and shoulder placement. The important boundary is that the visual system does not invent another timing signal. The meter and the windup should explain one state together. That still leaves room for personality. Different characters can vary their idle movement, follow-through, and reaction without gaining stronger gameplay attributes. In SlapLoop, all 12 characters share those attributes; the differences are visual and expressive. Feedback needs a useful vocabulary The initial result display relied heavily on percentages. A precise number was easy to calculate, but a string of percentages was not an especially memorable match summary. Timing grades give players a more useful vocabulary for comparing their hits. There is an important distinction, though: a timing grade describes the original input, while effective power and damage can also be affected by tactics or a skill. Changing damage should not silently rewrite the timing feedback into a better grade. The same separation helps with the health bars. Distinct colors identify the player and CPU, and changing colors make low health easier to notice. Numeric health can support that signal without being the only way to read it. Waiting is also a game state A browser tab is easy to leave idle. The current match has a 15-second automatic-slap timer so that a turn does not wait indefinitely for input. When time expires, the game swings at the power available at that moment. This should be visible before it happens. A countdown inside the arena explains the deadline; pause controls provide an explicit way to take a break. The result is more predictable than an unexplained automatic action after a long silence. Input, timing, and pause behavior also need to agree on when an action is accepted. An interesting animation cannot compensate for a release that appears to count twice or lands after the match has already changed state. What I would check in ...

Powered by Readybot.io

**DEV Community** ## Moving a browser game's controls into the …

DEV Community

AWS CloudWatch Alarms and SNS: Get Notified When Something Goes Wrong

In the last two posts I set up several accounts and a way for people to sign in. Now the question every real environment hits: when something goes wrong, who finds out, and how fast? The basic answer on AWS is a CloudWatch alarm that sends a message through Amazon SNS. What each service does Amazon CloudWatch collects metrics, such as CPU use or estimated charges, and can watch them with alarms. CloudWatch alarm compares a metric to a threshold and moves between three states: OK, ALARM and INSUFFICIENT_DATA. Amazon SNS (Simple Notification Service) sends a message to everyone subscribed to a topic, by email, SMS or other targets. The building blocks Term What it is Notes Metric A number measured over time Example: CPUUtilization or EstimatedCharges Alarm A rule on one metric Threshold, period and number of periods SNS topic A channel for messages Alarms publish to it Subscription Who receives the topic An email subscription must be confirmed Alarm action What the alarm does on a state change Often: publish to an SNS topic Why a topic and not a direct email One topic can notify a whole team, and you can add people without touching the alarm. The same topic can be reused by many alarms. Later you can add other targets, such as a chat tool or a Lambda function. Hands-on: a billing alert in your own account Sign in with a user that can open the Billing console, and turn on Receive CloudWatch billing alerts in Billing preferences. Switch the Region to US East (N. Virginia), because billing metrics are only published there. Open SNS, create a Standard topic named billing-alerts, and add an Email subscription with your address. Open the email and choose Confirm subscription. Until you do, nothing is delivered. Open CloudWatch, then Alarms, then Create alarm. Select the metric Billing > Total Estimated Charge and set a threshold such as 5 USD. For the alarm action choose In alarm, send to the billing-alerts topic, and create the alarm. Test the notification path with the CLI: aws cloudwatch set-alarm-state --alarm-name --state-value ALARM --state-reason "test" and check that the email arrives. Common mistakes Forgetting to confirm the email subscription. Creating the billing alarm in a Region other than US East (N. Virginia). Setting the threshold so high that the alarm never fires. Sending alerts to one personal inbox that nobody else watches. Key takeaways An alarm watches a metric, SNS delivers the message. Use a topic so you can add people and targets later. Confirm subscriptions and test the path on purpose. Start with a billing alarm in every new account. Tomorrow I'll look at how to see who did what in your account, with AWS CloudTrail. Part of #120DaysOfAWS.

Powered by Readybot.io

**DEV Community** ## AWS CloudWatch Alarms and SNS: Get Notified …

DEV Community

I Built a 3D Chord Progression Generator That Visualizes Music Theory in Your Browser

TL;DR: Music theory is hard to visualize. The 3D Chord Progression Generator makes chords visible on a 3D keyboard, generates progressions by mood, and shows wave harmonics alongside traditional theory - all in your browser with no install required. Chords are easy to hear and hard to picture. You press three keys on a piano, sound comes out, and your ear knows whether it's happy or tense. But what are those notes doing together? Why does C-E-G sound different from C-E♭-G? And how do you move from one chord to the next without it sounding random? I wanted to see the notes light up on a 3D keyboard, with the wave, the harmonics and the theory beside them. So I built the 3D Chord Progression Generator. It runs in the browser. No install, no account, no downloads. You play a chord, pick a mood and a key to get a progression, then explore scales, modes and the circle of fifths. The sounds are made live in your browser, not from recordings. This article covers what the tool is, how it compares with a plain piano app and a theory book, a walk-through on desktop and phone, how I built it and what it cannot do. I built it, so this is written by the person who made it, and that is also why I list the limits at the end. What the 3D Chord Progression Generator is The 3D Chord Progression Generator is a music theory tool that runs in a web browser. It is designed and built by me, Julian Neagu, a solo founder in London who builds AI tools, apps and websites under VisionVix. I made it for learners, songwriters and anyone curious about how chords work. A 3D piano sits in the middle of the screen. Controls surround it. Every panel reacts to what you play, so the notes you press show up as a wave, as harmonics and as a named chord. The header says it plainly: 3D keyboard, 7 instruments, chords, scales, theory. The keyboard spans 25 keys from C3 to C5 - two full octaves. Seven instruments are available, each with customizable parameters for warmth, brightness, and air noise. The analyzer shows the waveform, spectrum, harmonics, and theory in real time. In short: open VisionVix World, press Open Simulators, and choose Chord Progression Generator. You get a 3D keyboard, seven instruments, an analyzer that shows the wave and harmonics of what you play, progressions for six moods, 14 scales and modes, and a circle of fifths. Piano app vs theory book vs 3D chord tool There are three common ways to learn chords, and they do different jobs. Feature Plain piano app Theory book 3D chord tool You can Play notes Read the rules Play and see the rules react Sound One piano None Seven instruments Chords You find them Listed in tables Named as you play them Progressions You write them Examples on a page Generated by mood and key Where Phone or tablet On a shelf Any browser, laptop or phone The tool does not teach music on its own. It gives you something to press, hear and look at while you read or practice. A theory book tells you that a major chord is built from the root, major third and perfect fifth. The 3D Chord Progression Generator shows you those intervals lighting up on the keyboard, labels the chord as "C Major" and displays the frequency ratios. The tool bridges the gap between reading rules and hearing sound. You press, you see, you hear - all at once. If you want to see more of my projects in action, I share walkthroughs and builds on my YouTube channel, where I break down other tools like this one. A walk-through of the 3D Chord Progression Generator 1. Open the tool Everything starts at the VisionVix landing page. The hero shows the 3D Simulation Platform with a green Open Simulators button under the headline. It runs the tools right in your browser, with nothing to install. Press Open Simulators. You arrive at the Choose Your 3D World Simulator page. At the time of writing it shows nine worlds: seven live, two coming soon. Each card has a green Enter Simulator button. Press it on the Chord Progression Generator card. On a phone, the steps a...

Powered by Readybot.io

**DEV Community** ## I Built a 3D Chord Progression Generator …

DEV Community

Consistency beats talent in coding

When I started, I thought great developers were just born smarter. After years of building with Flutter, React.js, and Node.js, I believe something different: consistency matters more than talent. Coding is a skill, and skills grow with regular practice. One hour a day for a year will take you much further than a 12-hour binge once a month. What consistency looks like: Small daily practice: Even 30-60 minutes counts. Show up on the days you don’t feel like it. Building, not just watching: Try to write some code every day, even a small feature or a bug fix. Tracking progress: Keep a GitHub streak, a notes file, or a simple checklist so you can see how far you’ve come. Learning one thing at a time: Finish one topic before jumping to the next shiny tool. Accepting slow days: Some days you’ll understand little. That’s part of the process, so don’t quit. Why it works: Concepts that confuse you today become obvious in a few weeks. Small wins stack up, and then one day you notice you can build things you couldn’t before. You don’t need to be the smartest in the room. You just need to keep showing up. What’s your daily coding habit? Share it below, it might help someone else stay on track. Coding #Consistency #LearnToCode #Programming #DeveloperJourney

Powered by Readybot.io

**DEV Community** ## Consistency beats talent in coding When I …

DEV Community

RefMod Without LoRA Training: Bundling a Character, a Fight Move, a Voice and a Set Into Loadable Files

The usual way to make a model hold a specific character or perform a specific fight move is to train a LoRA. RefMod does something different: it stores the reference as a loadable modification file that conditions the model at inference time. The practical difference is turnaround. A LoRA takes a training run; a RefMod takes the source material and the extraction pass. What goes into a RefMod Six workflows in this episode, in four categories: # Workflow Reference type 01 Character RefMod Single image or folder batch 02 Action RefMod Single video or folder batch 03 Audio RefMod Single audio clip 04 Martial-arts-hall scene RefMod Two camera angles, batch 05 Multi-reference gen A — Apply Applies a stack of RefMods 06 Multi-reference gen B — TextEncode Encodes the textual half of the conditioning The A/B split in 05 and 06 matters: applying a reference and describing it are two separate stages, and you can change one without rebuilding the other. Where it is actually strong Character: holds identity across shots. Face structure, hair, and the read of the outfit survive camera changes. Action: a fight move transfers as motion, not as a still pose. That is the hard part and the reason this is interesting. Scene: a fixed set with two camera angles means you can cut between coverage without the room changing. Where it is weaker Audio RefMod is the least reliable of the four. Timbre transfers, but the cadence of the source clip leaks into the output — if the reference was recorded quickly, the result sounds rushed regardless of what the text says. The realistic workflow Use RefMod to get 90% there, then fix the remaining 10% with a short LoRA only if you need it. That order saves the training run in most cases. Files HF: https://huggingface.co/AI-KSK/minimax-h3-refmod-workflows ModelScope: https://modelscope.cn/models/Sunjian520/minimax-h3-refmod-workflows Video walkthrough (Chinese): https://youtu.be/H5L5sO7dRu8 Bilibili: https://www.bilibili.com/video/BV1F2nZ6SEA7/ Notes ComfyUI exports; the H3 model weights must exist locally. Tutorial material, not a product. Independent AI-KSK release; not affiliated with the model authors or RunningHub.

Powered by Readybot.io

**DEV Community** ## RefMod Without LoRA Training: Bundling a Character, …

DEV Community

How to calculate print PPI after a Canvas crop

A 6,000 × 4,000 px landscape photo has 600 source pixels per inch at 10 inches wide. But if you want it to fill a 10 × 8-inch print, the photo's 3:2 rectangle is wider than the 5:4 print. A full-bleed export needs a crop. What matters for sharpness is how many source pixels survive that crop. Calculate the crop before creating the canvas For a full-bleed print, let the shorter supply of pixels set the scale. Divide each decoded image dimension by the corresponding print dimension, then take the smaller result. That gives the source pixels per inch for the crop. Multiplying the print dimensions back by that value gives the width and height of the source rectangle. function printCrop(sw, sh, widthIn, heightIn) {
for (const n of [sw, sh, widthIn, heightIn]) {
if (!Number.isFinite(n) || n <= 0) {
throw new RangeError("Image and print dimensions must be positive");
}
}

const sourcePpi = Math.min(sw / widthIn, sh / heightIn);
const cropWidth = Math.min(sw, widthIn * sourcePpi);
const cropHeight = Math.min(sh, heightIn * sourcePpi);

return {
sourcePpi,
cropWidth,
cropHeight,
x: (sw - cropWidth) / 2,
y: (sh - cropHeight) / 2,
};
}

const crop = printCrop(6000, 4000, 10, 8);
// 500 PPI, with a 5000 × 4000 crop beginning at (500, 0).
At 10 × 8 inches, exactly 1,000 pixels of source width are outside the crop. If the subject is near one side, change x within 0 to sw - cropWidth to keep it in frame. Moving the crop changes what is visible. The resolution stays the same until the crop dimensions change. Draw it at the delivery resolution Keep the source crop in image pixels. Size the output canvas separately based on what the lab requests. Here a 10 × 8-inch print at 300 PPI becomes a 3,000 × 2,400 px image. The nine-argument form of Canvas drawImage takes both rectangles explicitly. // bitmap is a decoded image, such as an ImageBitmap.
const widthIn = 10;
const heightIn = 8;
const requestedPpi = 300;
const source = printCrop(bitmap.width, bitmap.height, widthIn, heightIn);

const canvas = document.createElement("canvas");
canvas.width = Math.round(widthIn * requestedPpi);
canvas.height = Math.round(heightIn * requestedPpi);

canvas.getContext("2d").drawImage(
bitmap,
source.x, source.y, source.cropWidth, source.cropHeight,
0, 0, canvas.width, canvas.height,
);
Here the canvas downsamples a 500 PPI source crop. With a 2,400 × 1,600 px original, the same print would have 200 source PPI. Drawing that smaller image into a 3,000 × 2,400 output can satisfy a file-dimension requirement, but the added pixels do not recover source detail. If you display a quality warning, base it on sourcePpi. These calculations apply to a crop that fills the print. A fit-with-borders preview is a different layout. When exporting, check the lab's required pixel dimensions and selected print size. A browser export's density metadata may differ from the PPI used to size the canvas. AI assistance was used to draft and edit this article. The calculations and code examples were checked before publication.

Powered by Readybot.io

**DEV Community** ## How to calculate print PPI after a …

DEV Community

An Advisory-to-Evidence Triage Playbook for CVE-2026-112687

An Advisory-to-Evidence Triage Playbook for CVE-2026-112687 An advisory creates work before it creates certainty. For CVE-2026-112687, the confirmed facts are concise: a MediaRemote issue, a local-network attacker condition, possible system termination or process memory corruption, and fixes in iOS 27.2, iPadOS 27.2, and tvOS 27.2. The playbook should turn those facts into decisions without pretending to know the unpublished trigger. Security operations teams can use five gates: scope, exposure, patch evidence, observation, and closure. Each gate produces a record that another owner can review. Gate 1: define the supported inventory Start with Apple's affected model list. The iOS and iPadOS advisory names iPhone 11 and later and multiple iPad Pro, iPad Air, iPad, and iPad mini generations. The tvOS advisory names Apple TV 4K second generation and later. Merge endpoint-management data, procurement records, network observations, and owner interviews. Deduplicate by device identity rather than user name. Mark devices with unknown ownership or stale check-ins as unresolved, not compliant. The gate closes when the team has a defined population and a named owner for every management class: supervised endpoints, personal devices, and media appliances. Gate 2: model local reachability The advisory says an attacker on the local network may cause the documented impact. It does not identify a packet signature, so the team should map possible local paths rather than search for one known exploit string. Collect wireless segments, routing boundaries, guest isolation, peer-to-peer policy, and management subnet rules for the device population. Identify systems that can initiate communication with the Apple devices and whether the business needs those paths. A reachability review can support two decisions: which devices have a broader local peer population, and which network changes can reduce exposure while patching. No network map alone proves that an attack occurred. Gate 3: obtain current build evidence The fixed release family is 27.2. A valid record includes the reported OS version, device model, last check-in time, and source. Sample devices where management coverage is ambiguous or the record changed recently. For personal devices, collect only the minimum status allowed by policy. For Apple TV, confirm the physical device and location, because a management entry may outlive the hardware. A build below 27.2 creates a remediation task. It does not create a compromise finding. Keep those statuses separate so that a patch exception is not mislabeled as an incident. Gate 4: review observable effects The documented outcomes are unexpected system termination and process-memory corruption. Review crash or diagnostic records involving media services, especially repeated failures after a local-network change or near a suspicious host. Enrich each candidate with the device build, network peers, applications in use, and source logs. Preserve evidence before rebooting or replacing a device when a credible incident lead exists. Use the same review on fixed devices as a control. A repeated failure in both fixed and vulnerable populations may indicate a different cause. The public record does not provide a safe reproduction method, so do not attempt to generate a crash from a speculative trigger. Gate 5: assign an outcome and a next step Each device should end in one of four states: patched with current evidence, remediation pending with an owner, an approved business exception with containment, or an incident candidate under investigation. These states answer different questions and should not collapse into a single "risk" label. A business exception requires more than a recorded reason. It needs a network restriction or compensating control, an accountable owner, and a review date. An incident candidate needs evidence preservation, scope analysis, and a decision about whether the case is malicious, benign, or still unknown. Use exposure data wi...

Powered by Readybot.io

**DEV Community** ## An Advisory-to-Evidence Triage Playbook for CVE-2026-112687 An …

IGN India Pc

Dragon Ball FighterZ - Official Balance Update Trailer

A new balance update is coming soon to DRAGON BALL FighterZ! Check out this new trailer for DRAGON BALL FighterZ, the 3v3 tag fighting game developed by Arc System Works! DRAGON BALL FighterZ is available on PlayStation 4 (PS4), PlayStation 5 (PS5), PC (Steam), Nintendo Switch, Xbox One, and Xbox Series X|S!

Powered by Readybot.io

**IGN India Pc** ## Dragon Ball FighterZ - Official Balance …

DEV Community

Build an LLM From Scratch in 5 Minutes on a Free GPU: I Trained a 5.9-Million-Parameter Storyteller, and Here Is What It Wrote

Part 1 of ten. Each post is a short, plain-words tour of one lesson from e=mc², a free build-an-LLM-from-scratch course; the full lesson, notebook and numbers are on the site. Most people think building a language model takes a lab, a budget and a team. Last month I built one from nothing, on a free Google Colab GPU, in about five minutes end to end. It has 5.9 million parameters, it learned to write bedtime stories, and it is sitting on the Hugging Face Hub right now where you can run it. I want to show you what it wrote, what the numbers mean, and why doing this once makes every headline about trillion-parameter models stop sounding like magic. Meet Momo and Ella The lesson is told through two characters, because the ideas are simpler than the vocabulary. Momo is a young monkey learning to tell stories. Inside his head are millions of tiny dials, all pointing somewhere random, so his first stories are nonsense. Ella is an elephant, and elephants never forget. Every night she reads Momo a story one word at a time, stops before each word, lets him guess, and tells him how wrong he was. Then she nudges his dials a little. Thousands of nights later, Momo tells stories. That is the whole of machine learning in one paragraph. Momo is the model. His dials are the weights (5.9 million of them here). Ella's stories are the training data. "How wrong were you" is the loss. Nudging the dials is gradient descent. Everything else is detail, and the lesson decodes about forty of those details one at a time. What a language model does, in one sentence Given the words so far, guess the next one. A story is that guess repeated. A chat answer is the same thing with your question as the words so far. When Momo continues "Once upon a time, a little", four things happen. The sentence is bitten into tokens, pieces of text with a number on each (our vocabulary has 4,096). Each token becomes a spot on a meaning map, a list of 256 numbers where similar words sit close together. Then every token looks back at the tokens before it and decides how much to listen to each: "capital" finds "France" and borrows some of its meaning. That is attention, from the 2017 paper that started all this, and it runs six times in a row, four heads each. Finally the last token's sharpened meaning is scored against every word in the vocabulary, and one is picked. That is it. That is also, with more stories, more blocks and more GPUs, what the big ones do. The numbers, honestly 5.9 million parameters: 6 blocks, 4 attention heads, 256-wide, a vocabulary of 4,096 pieces learned from the stories themselves. 1,500 training steps, each a batch of 32 windows of 256 tokens. 1.2 minutes of training on Colab's free T4 GPU; about five minutes for the whole notebook; about two minutes on an Apple Silicon Mac. Perplexity 9 on 200 stories the model never saw. Perplexity is "between how many words is Momo still choosing". Random guessing would be 4,096. Watching that number fall from 4,096 to 9 in front of you is the moment the field stops being mysterious. The 200 quiz stories were held out before training, so nothing the model memorised can flatter the score. An earlier draft got that wrong, and fixing it is why I trust the 9. What it wrote Given "Lily had a red balloon.", one run produced this: She ran to her mom and gave her a big bite. They both ate the balloon and had the balloon. It was shiny and yellow. They ran to the park to play with. Grammatical, kid-flavoured, slightly unhinged. That is exactly what 5.9 million parameters and two minutes buy, and I think it is wonderful. Then I gave it "Once upon a time, a little monkey", and it wrote this: Momo was sad because he could not swim like Bo the bear cub. He sat in the tall grass with his feet in the water. Ella said, "Try again, little one. Every time you try, you learn a little more." She held him up with her trunk. Momo kicked and kicked. Soon he could swim all by himself. The end. Lovely, right? It is also, character for...

Powered by Readybot.io

**DEV Community** ## Build an LLM From Scratch in 5 …

DEV Community

7 tips I’d give to beginner developers

7 tips I’d give to beginner developers I’m a full stack developer working with Flutter, React.js, Node.js, MongoDB, and PostgreSQL. Here’s what I wish I’d known when I started: Build projects, don’t just watch tutorials. Tutorials feel productive, but you only learn when you get stuck and fix things yourself. Pick a small idea and build it. Learn the fundamentals first. Frameworks change fast. JavaScript, Dart, HTTP, databases, and Git stay useful for years. Read error messages. Most of the answer is usually in the error. Read it slowly before you search or ask for help. Learn Git early. Commit often, write clear messages, and push your work to GitHub. It becomes your portfolio too. Don’t chase every new tool. Pick one stack, get good at it, and then expand. Depth beats a long list of half-learned technologies. Use AI as a helper, not a replacement. AI tools are great for explaining code and speeding up work, but make sure you understand what they write. If you can’t explain it, you don’t own it. Be patient with yourself. Everyone feels lost sometimes. Getting stuck is not a sign you’re bad at this. It’s how the learning happens. Keep building, keep shipping, and keep learning a little every day. What’s the best tip you would give to someone just starting out? BeginnerDevelopers #WebDevelopment #Programming #CodingTips

Powered by Readybot.io

**DEV Community** ## 7 tips I’d give to beginner developers …

DEV Community

Migrating an existing Next.js app to Cache Components before Next.js 17

With Next.js 16.4 (October 2026), the Next.js team started recommending Cache Components for every app. New projects from create-next-app have it switched on, and the release notes say it will become the default in Next.js 17. If you have an existing App Router app, that gives you a choice: migrate now at your own pace, or do it under pressure during a major upgrade. This post walks through the migration by hand, in the order that keeps your app building the whole time. Short version: enable cacheComponents, run the codemod that opts every route out of validation, fix the few things that can't be deferred, then convert one route at a time. Nothing is cached any more unless you write use cache. Patch to 16.4.5 first. Last checked: 15 November 2026, against the Next.js 16.4 documentation. What actually changes In the older App Router model, caching was mostly implicit and set per route: export const dynamic, export const revalidate, fetchCache, and cache options on fetch. With Cache Components the rule flips: Nothing is cached unless you mark it with the 'use cache' directive. You mark the function or component that should be cached, instead of setting a route-wide config. Anything that needs the request (cookies, headers, search params) goes inside a boundary, so the rest of the page can be prerendered as a static shell and the request-specific part streams in. The Next.js team describes use cache as a component-level version of the Cache-Control header. Before you start Be on Next.js 16. Cache Components needs it. Coming from 15 or older, do the version upgrade first. Install 16.4.5 or later. The November 2026 security release fixed several cache-disclosure bugs in Cache Components. Details in my write-up of that release. Decide who drives. There's an official agent skill that does this migration feature by feature (npx skills add vercel/next.js --skill next-cache-components-adoption), and next upgrade --agent gives a coding agent version-specific guidance. Even if you use them, read on: you're the one reviewing the pull requests. The migration, step by step Step 1: turn it on // next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
cacheComponents: true,
partialPrefetching: true,
};

export default nextConfig;
The 16.4 release notes treat Partial Prefetching as part of the model, so enable both. If you previously used experimental.dynamicIO or experimental.useCache, cacheComponents replaces them. Your build will now fail on any route segment that still exports dynamic, revalidate or fetchCache. That's expected. The table in the next section tells you what to do with each. Step 2: opt every route out of validation In development, Next.js now checks whether each route can render instantly and reports what blocks it. On an existing app that's a lot of reports at once. One codemod defers them all: npx @next/codemod@canary cache-components-instant-false ./app
It adds export const instant = false to every page, layout and default file. Use ./src/app if your project has a src folder. A wrong path reports "0 ok" rather than failing, so check the file count it prints. instant = false means "this segment is allowed to block". It doesn't force the route to be dynamic. Step 3: fix what can't be deferred Synchronous, non-deterministic calls during prerender fail the build whether a route is opted out or not: new Date() and Date.now() Math.random() crypto.randomUUID() Move each one so it runs at request time: put the component that needs it inside and call connection() before it, or move it into a Client Component. Step 4: convert one route at a time Remove instant = false from a route, open it in development and read the dev overlay. Each insight names the component and offers one of two fixes: Cache it: add 'use cache' to the component or data function. Stream it: wrap it in . Repeat until no route opts out. One thing that's easy to miss: insights don't appear i...

Powered by Readybot.io

**DEV Community** ## Migrating an existing Next.js app to Cache …

DEV Community

Installing cPanel & WHM on a VPS: requirements, the install command and what breaks

Most cPanel installs that fail do so before the installer has really started. The command itself is one line; the work is in giving it a server it is willing to run on. This is the checklist we use, written for developers who are comfortable with SSH but have not set up cPanel & WHM from scratch before. Everything about requirements comes from cPanel's own documentation as it stood in November 2026, so check it again if you are reading this much later. First, decide whether you need it at all cPanel on your own VPS makes sense when you are hosting several sites, for yourself or for clients, and you want the WHM side: creating accounts, setting limits, handing each client their own login. It does not make much sense for one website. The arithmetic is simple. You pay for the VPS, and on top of that you pay cPanel for a licence every month, priced by the number of accounts. Even the single-account tier costs more per month than most shared cPanel hosting plans, which already include the licence and someone else patching the server. If you have one WordPress site and no wish to be root, shared cPanel hosting is cheaper and less work. Install cPanel on a VPS when you have outgrown that, not before. Pick a supported operating system cPanel is narrow about distributions. As of November 2026 the documentation lists: OS Versions Notes AlmaLinux OS 8 (cPanel 110+), 9 (114+), 10 (132+) The default choice for most people Ubuntu 24.04 LTS only PowerDNS is the only supported nameserver; AppArmor can stay on CloudLinux 7, 8, 9, 10 10 needs cPanel 134+; 7 cannot take cPanel 112 or later and is not recommended Rocky Linux none for new installs cPanel now says you can no longer install on Rocky Linux Debian is not on the list, and neither is any Ubuntu release other than 24.04. The Rocky Linux change catches people out because older tutorials still recommend it. If you have no reason to prefer otherwise, take AlmaLinux 9 or 10. One quirk: if the server's IP address already has an active CloudLinux licence, the installer converts AlmaLinux to CloudLinux. Add --skip-cloudlinux to the install command to prevent that. Hardware: minimum and what to actually buy From the AlmaLinux and Ubuntu requirement pages: CPU: 64-bit, 1.1 GHz minimum (Ubuntu page recommends 2 GHz). ARM is not supported. RAM: 2 GB minimum, 4 GB recommended. If you plan to run ClamAV, cPanel strongly recommends at least 3 GB. Below the minimum, the installer refuses to continue. Disk: 20 GB minimum, 40 GB recommended, plus at least 5 GB free in /usr/local/cpanel for installs and upgrades. File system: XFS or ext4. On a single partition, at least 1,000,000 inodes, plus 50,000 per cPanel account. Network: a static, public IPv4 address. IPv6-only servers are not supported, DHCP-assigned addresses are not allowed, and cPanel only licenses publicly visible static IPs with working DNS. Behind NAT, you need 1:1 NAT with NAT loopback. In practice, 2 GB is enough to finish the install and not much more. Start at 4 GB and 40 GB if real sites and mail will live on the box. Start from a fresh OS This is the rule people most want to skip, and it is not optional. cPanel's installation guide says to install only on a freshly installed operating system, and warns that services installed before or during the install cause compatibility problems. That means no Apache, Nginx, MySQL, Postfix or Docker stack you set up "just to test". The installer brings its own web, mail, DNS and database services. There is also no uninstaller. cPanel's documentation says removing it means reformatting the server. So if the VPS has history, back up what you need and reinstall the image. Set a proper hostname The hostname must be a fully qualified domain name on a domain you have registered, must not be the same as any domain you will host on the server, can be at most 60 characters, and must resolve to the server's IP. Use a subdomain, not the bare domain. Create the A record (and AAAA if you use IPv6) first, th...

Powered by Readybot.io

**DEV Community** ## Installing cPanel & WHM on a VPS: …

DEV Community

🐾 DEV Pet Corner #6: Show Us Your Pets!

TL;DR DEV Pet Corner is back for another Sunday! It's a weekly series where we take a little break from tech and share our love for animals. Share pictures, stories, and memories of your pets, animals you've met, or adorable animals you follow online. Share as many pictures and links as you'd like! You don't need to own a pet to join in. If animals make you smile, you're welcome here ❤️ Table of Contents ❤️ Thank you for the love 🐶 Show us your animal friends! 🐾 See you next Sunday! ❤️ Thank you for the love! Hey everyone 👋 It's Sunday again, and DEV Pet Corner is back for its 6th edition! First of all, thank you so much to everyone who joined the previous edition ❤️ I loved seeing all your pictures, reading your stories, and getting to know your animal friends. It's always so lovely to see everyone come together and share something they love. I'm really happy this little corner of DEV is bringing us together! Thank you for taking the time to share your pictures and a little piece of your lives with us. It really means a lot to me! If you missed the previous edition, you can still meet all the cuties here 👉 DEV Pet Corner #5 Now, let's do this again 😄 🐶 Show us your animal friends! I'd love to see your pets, whether they're the ones waiting for you at home, the ones you grew up with, or the ones who have a special place in your heart. And you don't need to have a pet of your own to join in! Maybe you've met a friendly dog on a walk, spotted a cat in your neighborhood, or come across an animal on campus that made you stop for a moment. Perhaps you follow an adorable animal account on Instagram or have a favorite animal video you keep going back to. If it involves an animal you love or something that makes you smile, I'd love to see it 😃 Share pictures, funny moments, sweet memories, little stories, or links to your favorite animal posts. Share as many pictures as you'd like. There's no limit! If you're sharing someone else's content, please link to the original post and respect the creator's wishes about sharing their work. A little note for returning readers: You might notice this section looks familiar 😄 I'll keep it in future editions, too, so new readers can easily see what they can share without going back to previous posts. As always, feel free to join in with anything you'd like or simply enjoy all the cuteness in the comments! 🐾 See you next Sunday! I'm so happy this little series is bringing people together over something we love. Thank you for being part of it and making each edition special ❤️ As always, let's keep the comments friendly and welcoming to everyone. I'm looking forward to seeing your animal friends and enjoying all the cuteness with you! Thank you for being here. See you next Sunday for DEV Pet Corner #7!

Powered by Readybot.io

**DEV Community** ## 🐾 DEV Pet Corner #6: Show Us …

DEV Community

I classified 63 item categories for shipping personal luggage to China - here is the open dataset (CC BY)

Most "can I ship X to China" guides answer one item at a time. I wanted to know what the whole set of answers looks like. So I took the 63 paired English / Chinese item Q&A pages we publish for our used-personal-luggage line, classified each direct answer into one of four mutually exclusive verdicts, reviewed all 63 rows a second time, and published the result as an open dataset. The split Verdict Count Share Accepted as personal luggage 33 52.4% Personal-use only; new or resale stock out of scope 6 9.5% Send the item list for confirmation first 15 23.8% Outside our luggage-only scope 9 14.3% Three things fell out of the data that I did not expect: The boundary is the condition of the item, not the object. The same cosmetic travels as personal luggage if it is opened and in use; new or resale stock is out of scope. 54 of 63 categories have some personal-use path. The 15 "check first" cases are not random. Eight involve lithium cells (cameras, drones, headphones, laptops, consoles, phones, power banks, tablets) and need a model or Wh rating. The rest cluster around oversize dimensions, declared value and quantity. The nine exclusions form two clean groups: eight resale / daigou categories (auto parts, formula, coffee, electronics, pet food, snacks, supplements, tea) and one regulated equipment category. Not a scattered blacklist. Method, in short Corpus: 63 English + 63 Chinese first-party item pages, snapshot 15 November 2026, after a service-scope convergence on 13 November. Each direct answer was assigned one verdict by explicit rules, then all 63 rows were reviewed individually; eight boundary cases were corrected (for example, "personal-use only" was initially misread as "out of scope"). Categories are an editorial grouping, not a customs commodity code. No third-party data, no estimates, no search-volume numbers. Important caveat: this index describes OUR service scope. It is not a customs ruling, not a list of import regulations and not legal or declaration advice. Verdicts can change; check the linked item page for current wording. What's in the download JSON: 63 bilingual rows with verdict, category, confirmation fields and both source URLs CSV: the same 63 rows flat, ready for a spreadsheet The report page also has the category x verdict cross-tab and a ready-made citation CC BY 4.0. Credit "Subao Luggage" and link to the report: https://subaog.com/en/report/luggage-item-admissibility-index-2026 Direct downloads: https://subaog.com/assets/downloads/luggage-item-admissibility-2026.json https://subaog.com/assets/downloads/luggage-item-admissibility-2026.csv Chinese edition: https://subaog.com/zh-cn/report/luggage-item-admissibility-index-2026 I run a China-bound consolidated line for used personal luggage and personal effects, so I am not a neutral observer - that is exactly why the methodology and the limits are written down. If you find a row that disagrees with its linked item page, tell me in the comments and I will fix the dataset.

Powered by Readybot.io

**DEV Community** ## I classified 63 item categories for shipping …

DEV Community

内容系统应保存真实标题:工程角度的思考

从工程角度看,内容系统保存真实标题而非从 URL slug 反推,是更稳健的设计选择。 首先,URL slug 往往经过简化处理——移除非 ASCII 字符、转小写、替换空格为连字符。这种有损转换是不可逆的,反推必然丢失原始信息。例如标题 "C++ 内存管理技巧" 变成 "c-memory-management-tips",反推只能得到无法还原的残损字符串。 其次,slug 生成规则多变。不同系统可能采用 kebab-case、snake_case 或添加日期前缀。若后期调整规则,依赖反推的下游系统将集体失效。 再者,国际化场景下,slug 可能被转译为 ASCII 或保留 Unicode,行为不一致。保存真实标题则提供单一可信数据源。 最后,SEO 和分享需求常要求标题与 slug 解耦。保存原标题让系统支持 A/B 测试、多语言版本和动态元数据,而无需重构存储层。 综上,显式存储标题以数据冗余换取系统可靠性和扩展性,是面向未来的工程实践。

Powered by Readybot.io

**DEV Community** ## 内容系统应保存真实标题:工程角度的思考 从工程角度看,内容系统保存真实标题而非从 URL slug 反推,是更稳健的设计选择。 首先,URL slug …

DEV Community

The clever retrieval trick lost. A boring if statement won.

I built a multi-agent freelance copilot, measured it properly, and the results embarrassed my favourite idea. GigBid reads freelance job posts, decides whether to bid, and writes a proposal where every claim must link to evidence from the freelancer's own portfolio. A person approves everything before it is sent. I built it as a coursework project (LangGraph, three MCP servers, a React front end), and I measured it against a simulator I also wrote. This is not a tutorial. It is about the one result that changed how I think about where the effort should go. The idea I was proud of A job post asks for four things: PyTorch, Hugging Face, NLP, and some Python. Searching a portfolio with the whole post feels sloppy; surely it is better to decompose the post into requirements, retrieve evidence for each one separately, and merge the results. It is the standard advice in the advanced-RAG literature, it is satisfying to build, and I built it first. I wrote my hypothesis down before running anything (it is committed in docs/adr/0002-rag-hypotheses.md): decomposition would beat the naive baseline on jobs with four requirements by at least five points of Recall@5. What actually happened Evaluated on 60 stratified jobs per persona, three personas, evidence-level metrics against gold projects: variant Recall@5 MRR naive dense retrieval 0.91 0.63 hybrid BM25 + dense 1.00 0.74 hybrid + cross-encoder rerank 1.00 0.92 requirement decomposition, first version (merged per chunk) 0.84 0.42 requirement decomposition, fixed (merged per project) 0.92 0.45 Decomposition did not just fail to help. It made retrieval worse than the baseline, by a wide margin. My first implementation merged per-requirement rankings at the chunk level, which scatters a project's chunks across lists; merging at project level raised recall back up, but ranking quality stayed far below simple hybrid-plus-rerank. An ablation made the cause unmistakable. I took the full composed pipeline (decompose + BM25 + rerank + outcome boost), MRR 0.47, and removed one component at a time. Removing BM25, the reranker, or the outcome boost changed almost nothing. Removing decomposition alone restored MRR to 0.92. Why? Because on this kind of corpus every skill appears in several projects. A query for "Hugging Face" alone retrieves every project that mentions it; merging four such lists rewards projects that match one requirement strongly over projects that match all four. The whole-post query already contains the conjunction, and the cross-encoder is exactly the tool that reads it. Splitting the question threw that information away. I kept the failed variant in the results, and the chunk-level version's file as well. A negative result that is measured beats a win that is not. The thing that moved the needle more While I was tuning retrieval, a different number was quietly bad. My bid-or-skip decision had precision of about 0.85: it was bidding on jobs it should skip. I assumed the cause was something subtle, like weak evidence matching. It was not subtle. Twenty-four of the failures on the dev set were low-ball jobs: budgets of five to twenty dollars that my system happily bid on, because the decision rule never compared the budget to the freelancer's own rate floor. Fit was fine. Risk was fine. The job was just not worth the paid Connects. The fix was a dozen lines of deterministic code: if the posted budget cannot reach the freelancer's minimum, skip. Bid-or-skip F1 on that sample went from 0.89 to 0.98, and failures from 30 to 6. No model changed. No prompt changed. Set that next to the retrieval work: a day of retrieval variants improved a ranking metric (MRR 0.63 to 0.92), while the largest real error in the decision sat somewhere none of those variants touched, and a boring comparison fixed it. I did not measure how much retrieval quality moves the final decision, so I will not claim it does not. Retrieval quality is easy to measure, so it gets optimised; a missing business rule ...

Powered by Readybot.io

**DEV Community** ## The clever retrieval trick lost. A boring …

DEV Community

Next.js November 2026 security release: are you affected, and how to patch (16.4.5 and 15.5.29)

On 3 November 2026, the Next.js team released 16.4.5 and 15.5.29 to fix seven security issues. The most serious is a cache bug: in apps that use use cache, data cached for one tenant or locale can be served to another. Four of the seven involve Cache Components and only affect Next.js 16. That used to be a small group. Since Next.js 16.4, every new project made with create-next-app has Cache Components on by default, so it's now worth checking rather than assuming. The other three also affect Next.js 15. Short version: run npm ls next. If you're on 16.x below 16.4.5, or 15.x below 15.5.29, upgrade and redeploy. If your app uses use cache together with root parameters, also clear your caches after the upgrade, because wrong entries can outlive the fix. Last checked: 15 November 2026, against the Next.js blog post and the seven GitHub advisories. The seven issues at a glance Issue Advisory and CVE Affected versions You're exposed if Cache disclosure across root parameters when unstable_cache and use cache are combined GHSA-mfr8-8qww-v76h, CVE-2026-119790 16.3.0 to 16.4.4 The same use cache function is called both directly and from inside unstable_cache, and it reads a root parameter Cache leak across root parameters in nested use cache functions GHSA-vc7v-qv4w-wrv8, CVE-2026-119351 16.2.0 to 16.4.4 Cache Components is on, and one cached function calls another that reads a root parameter Cache disclosure and poisoning through binary use cache arguments GHSA-rhh4-g77r-r9qj, CVE-2026-119349 16.0.0 to 16.4.4 You pass binary values such as Uint8Array or Blob to a use cache function Shared shell cache poisoning GHSA-vvgp-ww4w-46vf, CVE-2026-119350 16.4.0 to 16.4.4 Self-hosted with next start, cacheComponents: true, Partial Prefetching off or unset Response mix-up when forwarding fetched responses GHSA-pc58-hwjq-p385, CVE-2026-119348 16.0.0 to 16.4.4, and 15.x up to 15.5.28 Self-hosted behind an HTTP/1.1 reverse proxy that pipelines requests, and your app returns a fetched Response unchanged HttpOnly cookie values exposed on Edge routes GHSA-f3mh-rxg3-f5g2, CVE-2026-119346 16.0.0 to 16.4.4, and 15.0.0 to 15.5.28 An Edge Route Handler or Edge API route sets a cookie through NextResponse SVG rejection bypass in Image Optimization GHSA-7qx7-33qx-rqx2, CVE-2026-119347 16.0.0 to 16.4.4, and 15.0.0 to 15.5.28 Self-hosted, and the image optimizer can be given an attacker-supplied image A note on severity. The Next.js blog post labels the first issue "High Severity" and the other six medium. On GitHub, all seven advisories are rated Moderate, with CVSS scores between 6.0 and 6.9. Either way, none is a remote code execution bug like the two in the October release. What each one means in plain words Cached data served to the wrong tenant or locale A root parameter is a dynamic segment that sits before the root layout, for example the [tenant] in app/[tenant]/layout.tsx or the [lang] in app/[lang]/layout.tsx. Code reads it with next/root-params. Apps use it to decide whose data, or which language, a page shows. Three of the cache bugs break that separation: unstable_cache plus use cache. Just after a server process starts, an unstable_cache call can pick up the result of another request's use cache call that is still in flight. It then stores that result under a key that ignores the root parameter. From then on, requests for other tenants or locales get the same entry until it's revalidated or evicted. Nested use cache functions. An outer cached function can be stored under a key that leaves out the root parameter its inner function read. The advisory adds that entries written this way can ignore tag invalidation and cache lifetimes. Binary arguments. Two different Uint8Array or Blob arguments can produce the same cache key, so one user can get another's cached result. In all three, an attacker can't choose what leaks. Which data gets served depends on which request filled the cache first. Shared shell poisoning (self-hosted) On a self-hosted...

Powered by Readybot.io

**DEV Community** ## Next.js November 2026 security release: are you …