AI 资讯
Angular's Official Agent Skills Helps AI Coding Tools Write Modern Angular
Google's Angular team has released a repository called angular/skills, focusing on Agent Skills that enhance AI coding agents' ability to write modern Angular code. The repository includes skills for generating code and scaffolding applications, reinforcing current Angular conventions. It serves as a snapshot, aiming to improve AI suggestions by providing updated context. By Daniel Curtis
AI 资讯
How to Build a LinkedIn Outreach Pipeline (Without Getting Your Account Banned)
TL;DR: A LinkedIn outreach pipeline is a background worker that signs in with your own session, opens profiles, sends connection requests and messages on a schedule you control, and can post content straight to your feed. The hard was staying invisible to LinkedIn's detection. We got to our nineteenth build in about two weeks. Along the way, the session kept dying after three profiles (a device fingerprint mismatch), the stealth layer turned out to be detectable on its own, an authenticated proxy refused to connect, and Chrome froze in ways no timeout caught. This is every failure and the fix that finally held. We built a LinkedIn marketing pipeline inside Ozigi because our own go-to-market runs on it. I didn't just want it to be another tool; I needed it to send real messages to real people without getting my personal account flagged. The very first version we built worked for sourcing and reaching three leads, then the session died. The second version got past that and froze instead. This pattern repeated for two weeks and led us from building v1 of our LinkedIn worker to the current version 26. This article is like a cleaned-up version of our build log for educational purposes. If you are trying to reach people on LinkedIn from code, you will hit most of these walls in roughly this order. I will name the exact failure each time, because "it stopped working" helped me precisely never. What Does a LinkedIn Outreach Pipeline Actually Do? A complete LinkedIn outreach pipeline does four jobs: It signs in with your session cookie so LinkedIn sees you, not a script. It opens a lead's profile. It sends a connection request or a message, depending on whether you are already a first-degree connection. And it can publish a post to your feed. The first three are outreach. The fourth is content. They share the same infrastructure, which matters later. None of these look overly complicated logic. You click a button, type into a box, press send. But the reason this turned into
AI 资讯
How to use build-your-own-x: Master programming by recreating your favorite technologies from scratch.
Are you tired of just using frameworks and libraries without truly understanding how they work under the hood? Imagine gaining an unparalleled depth of knowledge and problem-solving skills by building your favorite technologies from scratch. Master Programming by Recreating Your Favorite Technologies From Scratch As developers, we spend a significant portion of our time using tools, frameworks, and libraries built by others. While incredibly efficient, this often creates a knowledge gap. We know how to use a tool, but not why it works the way it does, or what fundamental problems it solves. This is where the "build-your-own-X" (BYOX) philosophy comes in. It's a powerful learning strategy where you recreate simplified versions of existing technologies – be it a web server, a database, a version control system, or even a frontend framework – using only fundamental programming concepts. It's not about replacing established tools; it's about dissecting them, understanding their core principles, and in doing so, mastering the craft of programming itself. Why Bother? The Profound Benefits of Building Your Own Investing time in building your own versions of existing technologies offers a wealth of benefits that accelerate your growth as a developer: Deepened Understanding: No more black boxes
AI 资讯
Composer Update Is Not Safe Anymore
Saturday morning. I opened Twitter and saw a tweet about the Laravel-Lang packages being compromised. My first reaction was simple: "I don't use that package." Then I opened composer.json on a project I work on and found this in require-dev : "laravel-lang/lang" : "^14.8" , "laravel-lang/publisher" : "^16.8" , That changed things. What Actually Happened The attack used laravel-lang packages as the distribution channel. And the sneaky part: the main repository branch looked completely clean. No suspicious commits, no new code. The malicious payload was pushed via git tags on forks . Most developers would not notice anything. Just a routine composer update , same as always. Once installed, the payload executed at autoload time . That means every php artisan command, queue worker, or web request running that codebase triggered the malware the moment PHP hit require_once __DIR__.'/../vendor/autoload.php' in public/index.php . Silently. No error, no red screen, nothing. The malware was a credential stealer. It searched the machine for: .env files from Laravel projects AWS access keys and session tokens SSH private keys GitHub CLI tokens NPM tokens Infrastructure secrets This is not a SQL injection that messes with your database rows. This is a key stealer that runs on your machine and takes everything it finds. Aikido Security caught it and reported it to Packagist. Packagist removed the affected versions. But if you ran composer update during that window, you were exposed. Why Supply Chain Attacks Are Different Classic Laravel security talks about SQL injection, XSS, CSRF. Those are attacks that come from outside users sending malicious input to your application. Supply chain attacks come from inside your own development process. The attacker does not need to find a vulnerability in your code. They need to compromise one developer account at one package maintainer. Every project depending on that package is now exposed. With AI tools, these attacks are getting more soph
AI 资讯
I built an AI for relationships — here's why nobody else has
Every developer I know has built something for themselves. A productivity tool. A habit tracker. A personal finance app. An AI that makes them smarter, faster, calmer. I did the same thing for 2 years. Then I had a conversation with someone close to me that I completely mishandled — and I realised no amount of personal productivity tools would have helped me there. The problem wasn't me, individually. The problem was the space between us. So I started asking a weird question Why has all of AI been built for individuals? Copilot helps you code faster. ChatGPT makes you smarter. Notion AI organises your thoughts. Calm helps you sleep better. Not one of them is built for what happens when two people try to understand each other. That's a massive gap. And it's one I couldn't stop thinking about. What I built Mendle — an AI-powered Relationship Intelligence platform. Not a therapy app. Not a chatbot companion. Not another journaling tool with an AI skin on top. The core idea is **shared emotional memory. Most relationship apps are built around one person's perspective. You log your feelings. You get insights. Your partner is an afterthought in the architecture. Mendle is different at the data model level. Both people contribute. Both people benefit. The AI builds an understanding of the relationship not just an individual. Over time it surfaces patterns. Communication loops. Emotional triggers. The things you keep missing because you're too close to them. The technical challenge that surprised me Building AI for two people is fundamentally harder than building it for one. Single-user AI: one context window, one set of preferences, one voice to understand. Relationship AI: two different communication styles, two different emotional vocabularies, shared history that neither person has complete visibility into, and privacy boundaries that have to be respected even between partners. The shared memory architecture was the hardest part to get right. How do you build something
AI 资讯
We audited 49 Show HN launches. 38 had a critical bug on day one.
Originally published on the Prufa blog . In June 2026 we pointed Prufa's free audit at 50 products that had just launched on Show HN — every launch from the previous 30 days that earned at least 10 points. These are products at their moment of maximum attention: front page, real traffic, founders watching the comments. The headline numbers, from the 49 audits that completed (one site couldn't be reached by our runner): 100% of the 49 launches had at least one machine-verified finding. 78% — 38 of 49 — had at least one critical finding. 40 critical and 61 warning findings in total, every one verified by deterministic checks against captured browser evidence. No site is named in this post. The point isn't to embarrass anyone — it's that these failures are systematic, and if these teams have them on launch day, you probably do too. Methodology, briefly Each site got the same audit a free Prufa run does: a real browser loads the public pages, captures network traffic, console output, cookies, and response codes, and a fixed suite of deterministic checks grades the evidence. Same input, same verdict. Every number below is from a code-verified check — no LLM opinions are counted anywhere in this data. One honest caveat: our export keeps only the top findings per site, so the per-issue counts below are floors , not totals. The real numbers are equal or worse. What actually breaks at website launch: the numbers Sites affected (of 49) Finding Severity 38 No analytics events detected critical 24 No canonical link on entry page info 22 Cookies set without the Secure attribute warning 14 Broken links warning 12 No <h1> heading on entry page info 11 No robots.txt info 10 JavaScript console errors during page load warning 10 Missing meta description warning 8 Images missing alt text info 7 Missing Open Graph tags info 3 Tag container loads, but no analytics events fire warning 2 Canonical URL pointing to a different host critical The most common launch bug: analytics that record
AI 资讯
Hurl vs Postman: Git-Friendly API Testing With Proxy-Aware Egress (2026)
TL;DR: You'll learn how to replace Postman collections with plain-text Hurl files that live in Git, run in CI, and test your API’s geo behavior from any country. Debugging API issues always boils down to taking the tests you already have, running them from a different network or region, and comparing what your API receives. But I bet most of us can’t actually do that right away because our test infrastructure itself is fragmented. Your CI could be using a different config entirely from the “correct” one on a local dev machine, and half the collections may still point to a staging URL that changed months ago. Postman doesn’t produce diff-friendly artifacts, so even figuring out what changed is a full-on investigation. If any of that sounds familiar, I’d recommend finally taking the big step and replacing your Postman collection with Hurl — a command-line HTTP test runner where tests are plain **.hurl** text files that live in Git. For this tutorial, I’ll also walk you through multi-region testing — how to run those tests using a proxy to get a German egress IP, and see what the API actually returns from there. Everything below is self-contained, you’ll only need a new Git repo, then create the tests (three plaintext files.) The Test Suite at a Glance Create a folder (name it whatever you like — hurl-api-tests works) and these are the files we're going to be creating at its root . That folder is your Git repo root; paths in commands and CI are relative to it. Note how we’re using multiple .env files. If you're new to API testing, you'll find out why in a bit. hurl-api-tests/ ├── tests/ │ ├── health.hurl # smoke test -- Makes sure Hurl works, basic asserts. Skip if you want │ ├── auth-flow.hurl # Tests chaining + jsonpath capture + Bearer header │ └── geo-detail.hurl # Tests two egress profiles, real country/ASN diff (direct or proxied) ├── .env.local.example ├── .env.ci.example ├── .env.proxy-de.example ├── run-tests.mjs # cross-platform runner └── .github/workflows/a
AI 资讯
I Made My Website Charge AI Crawlers with HTTP 402. In 30 Days, 5,811 Came and 5 Paid.
I run a content site, do-and-coffee.com . Like everyone else, it gets scraped by AI crawlers. Instead of blocking them, I did something else: I put a paywall in front of the site that returns HTTP 402 Payment Required to bots, with machine-readable payment instructions. If a crawler pays a cent in USDC, it gets the article. If it doesn't, it gets the 402 and nothing else. Then I let it run for 30 days and watched. Here's what actually happened — and it's not the number you'd put on a pitch deck. TL;DR A Cloudflare Worker sits in front of the site. AI crawlers get 402 + x402 payment requirements ; humans and search bots pass through free. Payment is USDC on Base , $0.01 per article, verified and settled through Coinbase's CDP facilitator. 30-day result: 5,811 crawler requests, 5 paid, 5,806 served a 402. Revenue at $0.01/article ≈ $0.05 . The interesting part isn't the revenue. It's who paid: GPTBot paid 4 times out of 48 requests; ClaudeBot paid once out of 651. Architecture do-and-coffee.com/blog/article/* ─▶ x402 Worker (Cloudflare) │ has X-PAYMENT-RESPONSE? ───────────┤─▶ yes ─▶ proxy origin (200) KV cache hit (payer:url)? ─────────┤─▶ yes ─▶ proxy origin (200) no X-PAYMENT? ─────────────────────┤─▶ 402 + payment requirements has X-PAYMENT? ────────────────────┘ │ ├─▶ CDP /verify (is the signed payment valid?) ├─▶ CDP /settle (waitUntil: confirmed — on-chain) └─▶ on success: KV.put(payer:url, receipt, ttl 24h) ─▶ proxy origin The worker speaks the x402 protocol: a 402 response carries an accepts array describing exactly how to pay (scheme exact , network base , asset USDC, amount, payTo wallet). A compliant agent reads that, signs a USDC payment, and retries with an X-PAYMENT header. The worker verifies and settles it through Coinbase's facilitator, then proxies the real article. How it works The 402 response When there's no payment, the worker builds the requirements and returns 402: function buildPaymentRequirements ( resourceUrl : string , env : Env ): Payment
AI 资讯
Summing 50,000 emission line items in the wrong order changes your total
Floating-point addition isn't associative. For a corporate inventory with tens of thousands of rows, naive summation drifts — and the number you disclose depends on row order. Here's why, and the fix. Here's a result that should bother anyone building carbon software. Take a corporate emissions inventory — tens of thousands of line items, each a number in tonnes CO₂e. Sum it. Now sort the same rows differently and sum again. The totals don't match. Not by much — maybe the third or fourth decimal place — but they don't match, and nothing in your code changed except the order. If you've never seen this, open a console: 0.1 + 0.2 === 0.3 // false That's the same bug, scaled up to a reporting deliverable. Why order changes the answer IEEE 754 doubles have 52 bits of mantissa. That's about 15–16 significant decimal digits of precision — generous, until you add numbers of very different magnitudes. When you add a small number to a large running total, the small number gets shifted right to line up the exponents before the addition happens. Bits that fall off the end of the mantissa are gone. Add a 0.0001 tCO₂e line to a running total of 80000.0 and there simply aren't enough mantissa bits to hold both the 80,000 and the 0.0001 — the small value is partially or completely swallowed. Float addition, as a result, isn't associative. (a + b) + c is not guaranteed to equal a + (b + c) . Sum your rows largest-first and the small values vanish early against a big accumulator. Sum smallest-first and they accumulate into something large enough to survive. Same data, different total. Here's the effect, deliberately constructed to be visible: const big = 80000 ; const smalls = Array ( 50000 ). fill ( 0.0001 ); // small values first, then the big one let a = 0 ; for ( const x of [... smalls , big ]) a += x ; // big value first, then the smalls let b = 0 ; for ( const x of [ big , ... smalls ]) b += x ; console . log ( a ); // 80004.99999999... console . log ( b ); // 80004.99999999...
AI 资讯
HTML/CSS Animation to Video (MP4): the Headless, Deterministic Way (incl. Claude)
So you asked Claude to animate something. Maybe a logo, a loading screen, a data viz. It spat out a neat HTML file with CSS keyframes, everything looks crisp in the browser — and now you need it as an MP4. The obvious approach is screen recording. Open QuickTime or OBS, hit record, play the animation, stop, trim. Works, kind of. Except it's not frame-perfect. If your machine lags for half a second, that lag is baked into the video. The animation runs at whatever speed your CPU felt like that afternoon. Completely non-deterministic. And the moment you tweak something — wrong colour, timing off by 200ms — you're setting the whole thing up again, which is just tiring. Not to mention that every time you hit record you start at a slightly different frame, so swapping the asset in your video editor becomes a pain because nothing lines up the same way twice. There's a better way. You can use htmlrec — a CLI tool that renders HTML animations to video frame by frame, without touching your screen. It controls the browser clock directly, so every frame is captured at exactly the right moment regardless of your machine's load. Pixel-perfect, every single time. Install it with: brew install dsplce-co/tap/htmlrec ffmpeg How to convert an HTML animation to video The reliable way to convert an HTML animation to video is to render it headlessly, frame by frame, instead of screen-recording it. Point a tool at your HTML file, let it drive the browser clock, and capture each frame at an exact timestamp: hrec render animation.html -o out.mp4 This works for any self-contained HTML/CSS animation — a logo reveal, a loading screen, a chart, or anything an LLM like Claude generated for you. The full step-by-step is below. The workflow 1. Get your animation from Claude (skip if you already have an HTML animation) Ask Claude for whatever you need. Something like: "Create an HTML/CSS animation of a logo appearing with a fade and slight upward motion, black background, 3 seconds" You'll get back
AI 资讯
Stop Calling Yourself a Software Engineer If You've Never Shipped Anything
There's a quiet lie rotting at the heart of modern software development, and almost nobody wants to say it out loud: the industry is drowning in people who can ace a LeetCode interview, argue endlessly about architectural patterns, and hold strong opinions about tabs versus spaces — but who have never actually shipped a product that real people use. We've built an entire culture around the performance of engineering rather than the practice of it. Walk into any dev team today and you'll find brilliant people who can recite the SOLID principles from memory, who have strong feelings about hexagonal architecture, and who will spend three weeks bikeshedding a folder structure — but who have never felt the particular anxiety of watching your own code process a real user's real money. Never felt that accountability. Never shipped something and then lived with the consequences. That's a problem! The industry has confused credentials and vocabulary with competence. A computer science degree, a GitHub profile full of tutorial clones, and the ability to reverse a linked list in a whiteboard setting are now routinely mistaken for the ability to build software that matters. They're not the same thing. The people who actually make software work — who ship under pressure, who fix production bugs at 11pm, who make pragmatic calls with incomplete information — often look terrible on paper. They'll choose a boring, proven technology over the shiny new thing. They'll skip the elegant abstraction in favour of something they can debug in a hurry. They understand that a running system that's 80% right beats a perfect system that's still in design. AI has made this worse. Now you can generate plausible-looking code, confidently-written documentation, and technically-accurate architecture diagrams without ever having built a system that stayed up under real load, without ever having migrated a production database at 2am, without ever having owned something from idea to invoice. Knowing ho
AI 资讯
I Added an AI Gate Before Every git push with no-mistakes 🛡️
We are all using AI to write code now. Whether it's Claude Code, Aider, or Copilot, the speed is incredible. But there is a glaring bottleneck we don't talk about enough: AI code is often just slightly broken. 🤖💥 It forgets an import, misses a type definition, or fails a test. Usually, you only find out after you push to GitHub. Your CI/CD pipeline turns red 🔴, and you end up polluting your Git history with a dozen commits titled fix: linting or fix: missing test variable . I recently found a repository called no-mistakes that solves this brilliantly. It acts as a local proxy between your terminal and GitHub, forcing AI to test and fix its own code before anyone else sees it. Here is why it's worth a look. 👇 😩 The Problem With Traditional Workflows Right now, developers handle broken code in two ways: The CI/CD Walk of Shame 🚶: You push code, wait 5 minutes for GitHub Actions to fail, pull the error locally, fix it, and push again. Pre-commit Hooks (Husky) 🐶: You set up local hooks. When you try to commit, it yells at you about formatting and blocks the commit until you manually fix it. Both methods are passive . They tell you something is broken, but they leave the cleanup to you. When you're using an AI coding agent to generate the code in the first place, manually babysitting its output defeats the purpose. 🚀 Enter no-mistakes no-mistakes is a CLI tool that intercepts your git push . Instead of sending your code straight to origin, it routes it through a localized validation pipeline. ⚙️ How it actually works: 🧱 The Hidden Sandbox: When you trigger it, it creates a temporary git worktree in the background. Your active editor stays completely undisturbed. ✅ Validation: It runs your tests, linter, and build steps inside that isolated sandbox. 🔁 The AI Feedback Loop: If a test fails, it captures the error log and hands it back to an AI agent, essentially saying: "You broke this test. Fix it." 🟢 The Clean Push: Once the AI patches the code and all tests pass, it push
AI 资讯
Parallel AI Coding with Git Worktrees: Run Multiple Agents Without Conflicts
Parallel AI Coding with Git Worktrees: Run Multiple Agents Without Conflicts Most parallel AI development problems stem from a single architectural mistake: multiple agents sharing the same working directory. Teams spin up three Claude Code instances, point them at the same project folder, and watch as file writes collide, branch checkouts interrupt each other, and lock files corrupt. The symptom looks like a race condition. The root cause is filesystem design. Git worktrees solve this by giving each agent its own isolated working directory while sharing a single .git repository. This distinction is critical. Developers get parallel execution without the storage overhead of full clones, and agents operate on separate branches without stepping on each other's file handles. The pattern has existed since Git 2.5, but AI coding workflows finally make it essential infrastructure. The Collision Problem: Why Multiple AI Agents Can't Share a Working Directory When you run git checkout feature-A in a directory where another process is reading files, the filesystem state changes underneath that reader. The other process doesn't see atomic transitions—it sees partial writes, missing files, and inconsistent dependency graphs. TypeScript compilers fail with "Cannot find module" errors. Dev servers crash because watched files disappeared mid-read. Lock files from package managers become corrupted when two agents run npm install simultaneously on different branches with different dependency trees. The obvious solution—staggering agent execution so only one runs at a time—defeats the purpose of parallel development. Teams that try this pattern end up with AI agents waiting in queue, each one blocking the next until it finishes. The bottleneck shifts from human typing speed to serial execution, and the productivity gains evaporate. Full repository clones work but waste disk space. A 2GB monorepo cloned five times for five agents consumes 10GB of redundant Git objects. Sparse checkou
AI 资讯
Building a Real-Time Collaborative Kanban Board with React, TypeScript, and WebSockets
Modern teams expect software to update instantly. Nobody wants to refresh a page every few seconds to see whether a task has moved from "In Progress" to "Done." Applications like Trello, Jira, and Linear have trained users to expect real-time collaboration. In this tutorial, we'll build a simplified real-time Kanban board using React, TypeScript, and WebSockets. Along the way, we'll cover project structure, state management, optimistic UI updates, and handling concurrent changes from multiple users. What We're Building Our application will support: Creating tasks Drag-and-drop task movement Real-time synchronization between users Optimistic updates Type-safe frontend architecture Tech Stack Frontend React TypeScript Vite React DnD Zustand Backend Node.js Express Socket.IO Database PostgreSQL Why WebSockets Instead of Polling? Many developers start with polling: setInterval (() => { fetch ( " /tasks " ); }, 5000 ); This works, but it's inefficient. Problems include: Unnecessary network requests Delayed updates Increased server load Poor user experience WebSockets maintain a persistent connection between client and server. Instead of asking: "Any updates yet?" the server simply says: "Here's an update." The result is lower latency and fewer network requests. Project Structure A scalable React project should avoid putting everything into a single components folder. Here's a structure that works well: src/ ├── api/ ├── components/ ├── features/ │ ├── board/ │ ├── columns/ │ └── tasks/ ├── hooks/ ├── store/ ├── services/ ├── types/ └── utils/ This feature-based organization scales much better than organizing solely by file type. Setting Up React Create the project: npm create vite@latest kanban-board cd kanban-board npm install Install dependencies: npm install zustand socket.io-client react-dnd react-dnd-html5-backend Defining Task Types Type safety becomes increasingly valuable as applications grow. export interface Task { id : string ; title : string ; description : s
AI 资讯
Deploying Symfony 8 to cPanel Step by Step guide.
Table of Contents Introduction Double-check everything Configure your Environment Variables Install/Update your Vendors Clear your Symfony Cache Install symfony/apache-pack Update composer.json public directory Build the assets Update Kernel.php Upload the project to cPanel Final Thoughts Introduction I'm new to Symfony and recently, I deployed a Symfony app to a shared hosting environment running cPanel with no SSH access. I could not figure out the best way to do it, let alone find useful resources online as most of them are outdated and felt inefficient. I faced a lot of errors like: failing to load the app with a 500 Internal Server Error, some static assets not loading, etc. I figured it out in the end. This motivated me to write this post to reduce the headache for other Symfony newbies like me. Alright, let's get into it. Most deployment guides online assume you can run Composer, clear caches, execute migrations, and run Symfony commands directly on the server. In shared hosting environments, that is often not possible so, my examples will assume you don't have it installed on the server. 1. Double-check everything The first step to building for production is double-checking everything if it's in intact. Yes, this is very important. In my case, I was faced with some static image assets failing to load because of wrong reference which was ignored on dev mode. I had something like: asset('/images/<filename> ) which was working in dev mode but failed to load the image in prod. the paths had to be like: asset('images/'). This was after checking how I defined other assets. So avoid things like this before hand. 2. Configure your Environment Variables For this, we will use the dotenv:dump command which is not registered by default, so you must register first in your services: # config/services.yaml services : Symfony\Component\Dotenv\Command\DotenvDumpCommand : ~ Then, run the command below. After running this command, Symfony will create and load the .env.local.ph
AI 资讯
How to Convert JSON to XML Without Breaking Your Integration
Working with modern APIs means living in JSON. But the moment your project touches a legacy enterprise system - a bank, a government service, or a SOAP endpoint that hasn't changed in a decade - you're suddenly dealing with XML. The challenge isn't just swapping syntax; it's understanding where the two formats are structurally incompatible, and what breaks silently when you ignore that. Why JSON and XML Don't Simply Map to Each Other JSON is compact and type-aware - it distinguishes between numbers, booleans, strings, and arrays natively. XML is verbose, treats all content as text, and has no concept of arrays. It only has repeated sibling elements. This gap is where most conversion bugs are born. A JSON array with just one item can silently become a plain object if your converter doesn't handle the edge case explicitly. The Three Biggest Conversion Pitfalls First is array ambiguity - XML has no array type, so a JSON array becomes repeated sibling elements. A single-item array is indistinguishable from a plain object unless your converter explicitly preserves the list context. Second is type erasure - XML flattens numbers, booleans, and strings into plain text, destroying the type information that many downstream systems depend on. Third is the single root element rule - JSON can have multiple top-level keys, but every valid XML document must have exactly one root element wrapping everything else. Handling Arrays the Right Way Always nest array items inside a named parent element. A JSON users array should produce a parent element containing individual child elements. This structure makes the list unambiguous to any downstream XML parser and prevents silent data loss during round-trips. Escaping Special Characters Characters that are perfectly valid inside a JSON string will break an XML parser immediately. Your conversion logic must escape these four: less-than becomes <, greater-than becomes >, ampersand becomes &, and double-quote becomes ". Skipping even one of
AI 资讯
Building An Astro Blog
This article was originally published on hawksley.dev . I've owned the domain name hawksley.dev for a while now, but I've never done much with it aside from sending email. Over the weekend, I thought I might as well make good use of it and decided to create a blog. In the beginning, this site had a humble home page with some links to GitHub projects. A blog requires much more infrastructure for me to use it effectively. For one, it'd be great if I could just write my posts in Markdown and have them formatted by my project automatically. Having a look at the options available, the first that stood out was GitHub's Jekyll . It looked nice and had great integration with GitHub Pages, which I'm hosting with at the time of writing. However, it just felt too rigid. I needed something modern that I felt I could get my hands dirty with. Enter Astro. Why Astro In the grand scheme of things, the Astro framework is pretty new at just 5 years old. That hasn't prevented it from gaining popularity rapidly. It holds performance as a key design principle, anything that can be static will render statically. By default, it ships absolutely no JS to the browser, which felt perfect for my use case. I have no need for advertising or heavy tracking scripts weighing down my site. All I need is a place to write. Learning how to work with Astro was completely painless. I created a new GitHub repository and followed along with their very high-quality documentation to create a blog of my own. At the very end of it, I’d created a nice neat blog that loaded instantly and was easy to write for. I wasn't satisfied by using the tutorial's blog for my site, though, as it felt too cookie-cutter, and so I started again, now with confidence in the framework. The Design Decisions There were some definite design decisions I knew I wanted from the get-go. First-class light mode and dark mode support were a must. Plenty of blogs offered just one or the other, and after a bit of digging, it didn't seem tec
AI 资讯
Validate and Mask Environment Variables in Node.js and TypeScript
Configuring environment variables seems simple, but it frequently leads to two common issues in production-grade applications: Missing Variables: A developer adds a new variable locally but forgets to update the .env.example file. Other team members pull the changes, and their local environments crash. Leaked Credentials: A debug log like console.log(process.env) prints database connection strings or API tokens to the terminal, leaving them visible in plaintext logs. To solve this, we created @novaedgedigitallabs/envkit . It is a zero-dependency (other than Zod) utility that validates, loads, and masks environment variables, and keeps your example configurations updated automatically. Key Features Schema Validation: Define your environment variables with Zod to enforce types and formats. Log Masking: Automatically redact sensitive credentials in console output. Example Syncing: Automatically append missing variables to your .env.example file without overwriting comments or values. Module Support: Runs in ESM and CommonJS. Getting Started Install the package and Zod: npm install @novaedgedigitallabs/envkit zod Initialize your configuration in a file like env.ts : import { createEnv } from ' @novaedgedigitallabs/envkit ' ; import { z } from ' zod ' ; export const env = createEnv ({ schema : { DATABASE_URL : z . string (). url (), JWT_SECRET : z . string (). min ( 32 ), PORT : z . coerce . number (). default ( 3000 ), NODE_ENV : z . enum ([ ' development ' , ' production ' , ' test ' ]). default ( ' development ' ), }, secrets : [ ' JWT_SECRET ' , ' DATABASE_URL ' ], generateExample : true , // Appends new keys to .env.example at runtime }); Because the variables are validated using Zod, your editor will provide full TypeScript autocomplete and type safety: env . PORT ; // Resolved as a number env . JWT_SECRET ; // Resolved as a string Preventing Secret Leaks in Logs Logging environment configs is a common debugging habit. With envkit, any variables listed in the secre
AI 资讯
Road To KiwiEngine #15: Why I Care More About Systems Than Features
One of the reasons I often find myself disagreeing with modern software trends is that many conversations revolve around features. How many features does it have? How quickly can we add more? What can we put on the marketing page? What can we announce next? Features matter. But I care far more about systems. Because at the end of the day, people don't buy features. They buy outcomes. And outcomes come from systems. The Car Analogy One of the easiest ways to explain my thinking is with cars. A car is made up of thousands of individual components. An engine. A transmission. Suspension. Brakes. Fuel systems. Electrical systems. Cooling systems. Sensors. Wiring. Each component is important. But nobody walks into a dealership and says: "I'd like to purchase six pistons, a transmission housing, and a fuel injector." They buy a car. They buy transportation. They buy a complete system. The individual parts only matter because they contribute to the overall experience. The customer doesn't want to think about every moving piece. They want to get in, turn the key, and drive. Drivers and Mechanics This is where I think technology often loses its way. Users are drivers. Engineers are mechanics. A driver should be able to: Start the vehicle Fill it with fuel Check the oil Wash it Perform light maintenance That's about it. They shouldn't need to understand combustion timing, transmission gearing, or electrical diagnostics to get to work. The mechanic, however, lives in the details. They tune the system. They replace parts. They troubleshoot failures. They recommend upgrades. They understand how the pieces fit together. Technology is exactly the same in my mind. Users should be able to focus on their goals. Engineers should focus on the machinery. Features Are Parts This is where I think software conversations sometimes become backwards. A feature is a component. A login screen is a component. A dashboard is a component. A database is a component. An API is a component. AI integra
AI 资讯
I keep finding the same Stripe webhook bugs in SaaS launches
I keep finding the same Stripe webhook bugs in SaaS launches Most early SaaS billing bugs are not in Stripe Checkout itself. They are in the glue around it: trusting the success redirect instead of the signed webhook parsing JSON before signature verification missing idempotency for retry events reflecting verifier errors from unauthenticated webhook routes updating subscription state without a replay/audit trail letting "Pro" access drift from the payment source of truth Over the last few days I have been shipping small public fixes around exactly this class of problem. Recent examples: Morphix: Cloudflare Worker Stripe webhook with signature verification, Supabase subscription sync, and event idempotency ledger https://github.com/yiyuanlee/morphix/pull/25 Open Mercato: hardened unauthenticated payment/shipping provider webhooks against raw verifier error reflection and missing rate limiting https://github.com/open-mercato/open-mercato/pull/2680 Covenant: webhook signatures hardened against replay and secret rotation gaps https://github.com/wienerlabs/covenant/pull/229 Volunteerflow: made a Stripe invoice.paid Founders Circle counter update transactional instead of partially committing user/counter state https://github.com/ppppowers/volunteerflow-project/pull/49 The pattern is boring in the best possible way: payment systems should be boring. The 48-hour version For a small SaaS that is about to turn on paid plans, I can take a bounded payment assurance sprint: inspect Checkout / webhook / subscription state flow verify signed webhook handling and raw-body behavior add idempotency around Stripe retry events ensure subscription status and entitlement state have one source of truth add a small regression test or smoke script leave a deploy/runbook note so the next failure is diagnosable Fixed scopes I am taking: $2,000 / 48 hours: one payment path hardened and documented $5,000 / 5 days: full launch pass across Checkout, webhook, subscription mirror, Pro gate, pricin