How I pulled data out of a cross-origin iframe I couldn't touch
The feature was supposed to be the easy one. NotebookLM generates flashcards. My extension reads them...
The feature was supposed to be the easy one. NotebookLM generates flashcards. My extension reads them...
I self-host Umami for my SaaS, ParserBee . The main reason I picked it: privacy-friendly, cookie-less, first-party analytics that adblockers supposedly leave alone because the script comes from your own domain. That last assumption turned out to be wrong, and the reason is the subdomain name itself. Writing it up because the fix is small and the failure mode is silent. The problem My Umami instance ran at umami.parserbee.com , so the tracker was loaded like this: <script defer src= "https://umami.parserbee.com/script.js" data-website-id= "..." ></script> The EasyPrivacy filter list (used by uBlock Origin, Brave Shields, AdGuard, and most other blockers) contains a rule that matches the Umami tracker by hostname pattern, along the lines of: ||umami.*/script.js It doesn't target a specific company's server. It targets any host whose subdomain is literally named umami serving a file called script.js . Which is exactly how most of us name things when self-hosting: umami.mydomain.com , plausible.mydomain.com , matomo.mydomain.com . The filter lists know this convention, and they have rules for it. The result: every visitor with an adblocker never loads the script. No errors on your side, no console noise, nothing in the Umami logs. The traffic just quietly never shows up. I only caught it because signups were arriving from campaigns that Umami claimed nobody clicked; the server logs and Stripe disagreed with the analytics, and the server logs were right. The fix Serve the same Umami instance from a second hostname that no filter list matches. No proxying, no renaming files, no changes to the Umami install itself. I added u.parserbee.com as an alias for the same service: 1. DNS record. A CNAME (or A record) for u.parserbee.com pointing at the same server as the existing umami.parserbee.com . 2. Reverse proxy. Add the new hostname to the existing Umami site config so both route to the same instance. I run Umami in Docker behind Coolify, where this is just adding a second d
OpenAI just bought a cloud company, and if you only read the headline you'd miss the point. This deal tells you exactly where AI coding is heading: out of your editor and into the cloud. Here's what happened and why it matters. What OpenAI actually bought On June 11, 2026, OpenAI announced it's acquiring Ona, a German startup you might know by its old name, Gitpod. Terms weren't disclosed, and the deal still needs regulatory approval before it closes. Ona builds secure cloud environments where code can run. That's the whole reason OpenAI wanted it. Its Codex coding agent has grown fast, now past 5 million weekly active users, up from 3 million in April, and the jobs those agents run have gotten much longer. Tasks that used to take a few minutes now stretch into hours, sometimes days. An agent that works for hours can't live on your laptop. Close the lid and it dies. It needs a persistent place in the cloud to keep running. That's what Ona gives Codex, and notably, Ona's platform can run inside a customer's own cloud, which matters for enterprises that won't send code elsewhere. Ona's enterprise usage reportedly grew 13-fold this year, with clients like major banks and pharma companies. Why this is a bigger deal than it looks For years, AI coding meant an assistant inside your editor. Autocomplete, a chat panel, quick edits. The editor was the product. That era is ending. When agents run for hours on their own, the important question isn't "how good is the autocomplete." It's "where does the agent run, and for how long." The battleground is shifting from the editor to persistent cloud execution, and OpenAI just bought its way into that fight. The wider war OpenAI isn't alone in this move, which is why it feels like a real war now. Cursor already added cloud agents that run tasks without tying up your machine. Devin, from Cognition, was built cloud-first as an autonomous engineer from day one. Anthropic's Claude Code runs long, multi-step jobs and connects into your t
Something happened in 2026 that should be on every engineer's radar, especially if you work in finance, healthcare, government, or anywhere regulated. The US government labeled an American AI company a national security supply-chain risk. That had never happened before. Whatever you think of the politics, the professional lesson underneath it is big, so let me walk through it plainly. What actually happened The Department of Defense terminated a contract with Anthropic worth an estimated $200 million. The breakdown wasn't about price or performance. It was about how the military could use the AI. Anthropic's CEO, Dario Amodei, objected to a clause that allowed the military "any lawful use" of the model. Anthropic wanted the contract to explicitly rule out things like domestic mass surveillance and autonomous weapons, and refused to sign without those limits. The government wanted fewer restrictions. When talks broke down, the Secretary of War, Pete Hegseth, formally designated Anthropic a "Supply-Chain Risk to National Security." That label is normally reserved for foreign adversaries and hostile telecom vendors, think of how the US treated Huawei. This was the first time it had ever been applied to an American company, and reportedly the first time it was used because a company wouldn't agree to certain contract terms. OpenAI then stepped in to take the Pentagon work Anthropic had walked away from. There's a legal coda worth noting for accuracy. In March 2026, a federal judge granted a preliminary injunction against the government, writing that the designation looked like retaliation for Anthropic drawing public scrutiny, which she framed as a First Amendment problem. So the label is being contested in court, not settled fact. Why this matters beyond the headlines Strip away the politics and this is a case study in something every regulated team deals with: what happens when your AI vendor's policies collide with your requirements. Anthropic drew a hard line on use
Several companies suspected of selling DJI products under their own brands might soon be banned in the US.
🚀 JavaScript Fundamentals (Week-03): Building a Strong Foundation "Before learning frameworks like React or backend technologies like Node.js, it's essential to build a strong understanding of JavaScript fundamentals. A solid foundation makes advanced concepts much easier to learn." Introduction During Week-03 of my Full Stack Development learning journey, I focused on understanding the fundamental concepts of JavaScript . Instead of only writing code, I wanted to understand how JavaScript works behind the scenes . In this week, I learned about variables, hoisting, different types of scope, execution context, call stack, closures, the this keyword, call() , apply() , bind() , module patterns, and clean coding principles like DRY and KISS . In this blog, I'll explain each concept in a simple and beginner-friendly way with examples. 📚 Topics Covered What is JavaScript? Variables ( var , let , const ) Hoisting Scope Global Scope Function Scope Block Scope Lexical Scope Scope Chain Execution Context Call Stack The this Keyword call() , apply() , and bind() Closures Module Pattern Common var vs let Bugs DRY Principle KISS Principle Conclusion 📌 What is JavaScript? JavaScript is a high-level, interpreted, single-threaded programming language used to build interactive and dynamic web applications. Initially, JavaScript was designed to run only in web browsers, but today it can also run on servers using Node.js . Some common uses of JavaScript include: Creating interactive web pages Form validation DOM manipulation API communication Web application development Backend development with Node.js Example console . log ( " Hello, JavaScript! " ); Output Hello , JavaScript ! 📦 Variables Variables are containers used to store data. JavaScript provides three ways to declare variables: var let const var Characteristics Function Scoped Can be redeclared Can be reassigned Hoisted var name = " Sai " ; var name = " Rahul " ; console . log ( name ); Output Rahul let Characteristics Block
Strings & Text Processing: Text Isn't as Simple as It Looks If someone asks you what's the simplest type of data in programming, there's a good chance you'll say strings . After all, they're just text. A person's name is a string. An email address is a string. A password is a string. Even the message you're reading right now is just a collection of strings. At first glance, there doesn't seem to be much to learn. You create a string, print it, compare it with another string, and move on. But after writing a few programs, things start getting... strange. You convert "hello" into uppercase, yet the original string somehow stays the same. An emoji that looks like a single character suddenly reports a length of 2 . Joining thousands of small strings together makes your program unexpectedly slower. And then someone introduces you to something called Regex , which looks less like code and more like an ancient spell. None of these are bugs. They're simply the result of how strings actually work behind the scenes. In this lesson, we'll uncover those hidden details one by one. Surprise #1: Why Didn't the String Change? Take a look at this code. const original = " hello " ; const shouted = original . toUpperCase (); console . log ( original ); console . log ( shouted ); What would you expect the output to be? Many beginners think the output will look like this: HELLO HELLO But that's not what happens. Instead, JavaScript prints: hello HELLO The original string remains exactly as it was. Why? Because strings are immutable . What Does "Immutable" Mean? The word immutable simply means: Once something is created, it cannot be changed. Think of a printed book. If you want every occurrence of the word hello to become HELLO , you don't magically change the ink that's already on the paper. Instead, you print a new copy with the updated text. Strings behave in a similar way. Whenever you use methods like: toUpperCase() toLowerCase() replace() concat() the original string stays untouch
Hi, I designed and developed a spatial index that maps museum artefacts as connecting arcs/hyperlinks from their origin site to institutional location/sources. The Index specifically looks at western/euro-american institutions and maps their collection categorised by them under Islamic art, Asian/African art, ethnological collections, Middle East, South America etc. Started as my MA thesis in 2022, kept building since, mostly solo with a little funding. Fully open source, self-hosted, AGPL-3.0,
Optimizing RAG at Scale: Chunking, Retrieval, and the Bayesian Search That Cut Latency 40% How we moved from "semantic search + hope" to a measured, tunable retrieval pipeline with 95% recall@10 The RAG Reality Check Everyone ships RAG the same way: chunk by 512 tokens, embed with text-embedding-3-small , top-k=5, stuff into context. It works for demos. Then you hit production: Legal contracts: 512 tokens splits clauses mid-sentence API docs: 1000-token chunks drown signal in noise Customer tickets: Conversational context needs overlap, not fixed windows Latency: 500ms embedding + 200ms vector search + 300ms LLM = 1s+ per query We rebuilt our retrieval layer from first principles. Here's what actually moves metrics. Chunking: One Size Fits None # rag/chunking.py from abc import ABC , abstractmethod from dataclasses import dataclass @dataclass class Chunk : text : str metadata : dict token_count : int chunk_id : str class ChunkingStrategy ( ABC ): @abstractmethod def chunk ( self , document : str , metadata : dict ) -> list [ Chunk ]: ... class FixedTokenChunker ( ChunkingStrategy ): """ Baseline. Good for homogeneous content. """ def __init__ ( self , chunk_size = 512 , overlap = 50 ): self . chunk_size = chunk_size self . overlap = overlap class RecursiveChunker ( ChunkingStrategy ): """ Respects structure: markdown headers, code blocks, paragraphs. """ def __init__ ( self , separators = [ " \n ## " , " \n ### " , " \n\n " , " \n " , " " ], chunk_size = 512 ): self . separators = separators self . chunk_size = chunk_size class SemanticChunker ( ChunkingStrategy ): """ Uses embedding similarity to find natural boundaries. """ def __init__ ( self , model = " text-embedding-3-small " , threshold = 0.7 ): self . model = model self . threshold = threshold class AgenticChunker ( ChunkingStrategy ): """ LLM decides boundaries. Expensive but highest quality for complex docs. """ def __init__ ( self , model = " gpt-4o-mini " ): self . model = model Our production config by
Quick Answer The "8 glasses a day" rule was never a research finding — it originated from a 1945 recommendation that was widely misquoted. How much water you actually need depends on your body weight, activity level, and climate. Most adults are mildly dehydrated for most of the day without knowing it, because thirst only activates after a 1–2% fluid deficit is already established — the same level at which mood, concentration, and physical performance are measurably impaired in controlled trials. This article covers what the science actually says about your personal hydration target, what happens at different levels of dehydration, and when drinking too much becomes a real concern. Use the calculator at the end to find your number. The "8 Glasses a Day" Rule Has No Scientific Basis The origin of the 8×8 rule (eight 8-ounce glasses per day) is commonly traced to a 1945 Food and Nutrition Board recommendation that adults need roughly 2.5 litres of water daily — but the same document immediately noted that most of this quantity is already contained in food. The "drink 8 glasses" takeaway dropped the second half of the sentence entirely. The actual evidence-based reference comes from the Institute of Medicine's 2004 Dietary Reference Intakes report, which set adequate intake at 3.7 litres total water per day for men and 2.7 litres for women — including all sources: beverages, food, and metabolic water from digestion. Since roughly 20% typically comes from food (particularly fruits and vegetables), the drinking target comes out to approximately 2.5–3 litres for men and 1.8–2.2 litres for women at baseline — before accounting for anything that changes it. Institute of Medicine (US) Panel on Dietary Reference Intakes for Electrolytes and Water. Dietary Reference Intakes for Water, Potassium, Sodium, Chloride, and Sulfate. National Academies Press; 2005. How Your Lifestyle Changes What You Need The IOM baseline assumes a sedentary adult in a temperate climate eating a mixed
The first hour with a new repo is archaeology: open package.json , skim the Makefile, hunt for .env.example , grep workflows for secrets. , check Compose for ports. I already built focused tools for each of those questions. What I still wanted was one command that prints the whole map. coldstart coldstart is that overview — offline, no accounts, agent-friendly JSON. go install github.com/SybilGambleyyu/coldstart@latest coldstart coldstart -md >> ONBOARDING.md coldstart -json It reports: Runtimes — Node engines, Go version, Cargo edition, Python requires, .nvmrc , .tool-versions Run — preferred npm scripts, Makefile ## targets, just, Taskfile Ports — Compose, env *_PORT , script --port Env keys — from .env.example CI secrets/vars — from GitHub Actions workflows (builtins like GITHUB_TOKEN omitted) Overview vs drill-down Need Tool One-pager coldstart Full task map howrun Every port + live check projports Secrets vs gh secret list needsecrets Lockfile PR summary lockbrief Typical first hour: coldstart # then only if you need depth: howrun -f test projports -unique -check needsecrets -check Small binaries, MIT, no SaaS. If it saves a README scroll, it is working.
Measured 2026-07-21 on vllm/vllm-tpu:nightly (vLLM 0.23.1rc1.dev1076), a GCE flex-start ct6e-standard-1t (one TPU v6e chip, 32 GB HBM) in europe-west4-a. TL;DR The plain google/gemma-4-E2B-it serves well on one v6e chip; none of its QAT siblings load at all. The 2-billion-parameter "efficient" Gemma 4 sustains 213 tok/s for a single user with a 16 ms first token, scales to ~2,200 output tok/s across concurrent streams, handles OpenAI-style function calling — including parallel calls and refusal to hallucinate calls — without a miss, and answers simple vision questions accurately in ~200 ms. The QAT variants are a different story: the int4 compressed-tensors export hits an unimplemented quantization path, and the bf16 QAT export trips a loader bug — the Gemma 4 implementation demands per-layer norms that E2B's KV-sharing architecture legitimately doesn't have. Filed upstream as tpu-inference #3225 . One capability coupling to know about: with a reasoning parser configured, schema enforcement only engages when thinking is enabled — thinking-off requests sail through unconstrained with a 200 status. Config interaction, not TPU limitation; details below. 1. Getting to a serving endpoint The host is a GCE flex-start VM — capacity granted on request, billed until deleted, hard-stopped at a 4-hour max run, $1.35/chip-hour. A startup script installs Docker, pulls vllm/vllm-tpu:nightly , fetches the Hugging Face token from Secret Manager via the metadata server, and launches vLLM. Boot timeline: VM RUNNING at t+0 (200 GB boot disk — the 10 GB default cannot hold the vLLM image) → Docker installed ~t+1:00 → image pulled ~t+6:00 → weights downloaded, XLA compiled, health green ~t+8:30. Two environment quirks worth knowing: Direct SSH silently times out on some networks even when the VPC allows tcp:22 — the block is upstream of the VPC. IAP tunneling ( gcloud compute ssh --tunnel-through-iap ) rides over HTTPS and works; so does tunneling the API port with gcloud compute start-