AI 资讯
JWT auth without the confusion
The mental model that fixes everything JWT is just a token format . It is not authentication, not a session, and not a database. Once you separate those ideas, most of the pain disappears. A JWT is a JSON object that is signed. That's it. The payload holds claims like sub (subject) and exp (expiration). The signature proves the token wasn't tampered with. What JWT is not Not a session store : You can't revoke a JWT before it expires. If you need revocation, you need a blocklist or short expiry. Not a database : Don't stuff heavy data in the payload. It gets sent on every request. Not a magic bullet : It's a way to pass claims between parties without a shared server-side state. The three flows that matter 1. Access token only Simplest flow: login returns a JWT, client sends it in the Authorization header, server verifies it on every request. // server middleware (Express example) const jwt = require ( ' jsonwebtoken ' ); function auth ( req , res , next ) { const header = req . headers . authorization ; if ( ! header ) return res . status ( 401 ). json ({ error : ' No token ' }); const token = header . split ( ' ' )[ 1 ]; // Bearer <token> try { req . user = jwt . verify ( token , process . env . JWT_SECRET ); next (); } catch ( err ) { res . status ( 401 ). json ({ error : ' Invalid token ' }); } } Works fine for small apps, but every request hits your auth logic and the token can't be invalidated early. 2. Access + refresh token Common pattern for SPAs. Access token lives 15 minutes, refresh token lives 7 days. The refresh token is stored securely (httpOnly cookie) and used only to get a new access token. // issue tokens on login const accessToken = jwt . sign ({ userId }, process . env . JWT_SECRET , { expiresIn : ' 15m ' }); const refreshToken = jwt . sign ({ userId }, process . env . REFRESH_SECRET , { expiresIn : ' 7d ' }); res . json ({ accessToken }); res . cookie ( ' refreshToken ' , refreshToken , { httpOnly : true , secure : true , sameSite : ' strict ' })
开源项目
ARCLUX 🐳 — a codebase intelligence tool that refuses to guess published: false
If you've ever stared at a 15,000-file monorepo wondering "what actually breaks if I touch this...
开发者
Your `fetch()` in `beforeunload` is being silently dropped. Use `navigator.sendBeacon()`.
When a user closes a tab, submits a form, or clicks an external link, you often need to send one last...
开发者
My Best CLI Alternatives to Postman for API Testing in 2026
When I first started testing APIs, Postman was one of the first tools I reached for. It was...
AI 资讯
10 Website Performance and UX Problems That Cost Small Businesses Customers
Small business websites rarely fail because of one catastrophic bug. They fail from an accumulation of small, fixable problems — a slow hero image here, an unlabeled form field there, a broken tab order that quietly locks out keyboard users. None of it looks dramatic in a screenshot. All of it adds up to lost conversions. Working across client rebuilds and audits at Alynox, the same handful of issues show up repeatedly, regardless of industry. Here are ten of the most common, with the practical, mostly low-effort fixes that address them. Unoptimized Images Dragging Down Load Time The single most common performance killer on small business sites is still oversized images — a 4MB PNG hero banner exported straight from a design tool, served at full resolution to a phone screen 400px wide. Fix: html src="hero-800.webp" srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1600.webp 1600w" sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1600px" alt="Interior of the workshop showing custom furniture in progress" loading="lazy" width="1600" height="900" /> Convert to WebP or AVIF, generate a handful of responsive sizes, lazy-load anything below the fold, and always set explicit width/height to reserve space and avoid layout shift. No Real Mobile-First Design A lot of "responsive" small business sites are really desktop layouts that get squeezed with media queries until they technically fit a phone screen. Buttons end up too small to tap accurately, text wraps awkwardly, and nav menus overlap content. Fix: Design and build mobile-first — base styles for small screens, then progressively enhance with min-width media queries for larger viewports: css .card { padding: 1rem; } @media (min-width: 768px) { .card { padding: 2rem; } } Tap targets should be at least 44×44px (per WCAG and Apple/Google HIG guidance), with enough spacing between interactive elements to prevent mis-taps on smaller screens. Accessibility Treated as an Afterthought Missing alt text, low-contras
AI 资讯
One pass of my eval bills $9.14 on the API and $0 through the CLI
One pass of my board eval bills $9.14 on the Anthropic API. Through Claude Code it bills $0. Same model, claude-opus-4-8. That is 27 calls, and it is not an estimate. The CLI prints a total_cost_usd in its envelope: what the run would have cost on the API. It bills the subscription instead, so the number is a receipt for money nobody spent. The switch fixed something better than the bill Running with --output-format json and --json-schema rides the same structured-output machinery the API does, an internal forced tool call. Format reliability on my suite went from 7 out of 15 to 15 out of 15. The schema needs relaxing first: strip pattern , minLength , maxLength , minItems , maxItems , format and the $schema meta-ref, because the CLI validator rejects draft-2020-12. The strict version stays in Zod on the caller side, so nothing is actually loosened, the validation just moves to where it can run. One trap is worth the whole post If ANTHROPIC_API_KEY sits in the child process environment, the CLI quietly bills the API account rather than the subscription. Nothing errors. Nothing warns. The invoice arrives. It gets stripped explicitly at spawn. This is the failure mode I would look for first in anyone else's runner: the money leak is silent, and the only symptom is a bill at the end of the month for a run you believed was free. And the limit, which matters more than the savings This is a dev-loop tool. Anthropic's consumer terms prohibit automated access "except when you are accessing our Services via an Anthropic API Key or where we otherwise explicitly permit it", and the commercial terms governing API use do not cover consumer subscriptions. An eval runner on my own machine is the CLI used as designed. A shipped service is not. Iterate on the CLI, ship on the API. What does one pass of your eval suite cost, and does anyone know it? Originally published at dylan.merigaud.com .
AI 资讯
React Flow auto layout with dagre for custom, variable-size nodes
Variable-size nodes break dagre's centering, first paint flickers, and straight chains render with kinked edges. Here is the why and the fix for each. Every React Flow and dagre tutorial shows the same thing: uniform gray boxes, laid out in a neat tree, everything centered. You copy the pattern, wire it up, and it works. Then you replace the gray boxes with real cards. A title that wraps to two lines. A card with a description and one without. And the layout starts to look subtly wrong: a parent sits off-center from its children, edges bend where they should be straight, and everything flashes in the top-left corner for a frame before jumping into place. I hit all three building an approval-workflow graph for a fintech app. The nodes were cards with variable content, so none of the fixed-size assumptions held. It took a while to understand that these are three separate bugs with three separate causes. So here is each one, why it happens, and the fix. At the end: the small package where I put all of it, so you do not have to rebuild this. Why dagre centers nodes off-balance (and the bounding-box fix) dagre centers a parent on the barycenter of its children, meaning the average of their center positions. That is correct when every child is the same size. It is visibly wrong when they are not. Concrete numbers from a graph I probed. A parent with two children, one 40px tall and one 200px tall. dagre puts the children at centers y=20 and y=180, so the parent lands at their average, y=100. But the visual middle of that group, the midpoint of the bounding box from the top of the small child to the bottom of the tall one, is y=140. The parent is 40px off from where your eye says it should be, and the taller the imbalance, the worse it gets. The fix is a post-pass on dagre's output: for every parent with two or more children, recompute its cross-axis position as the midpoint of the children's bounding box, walking deepest rank first so children settle before their parents.
AI 资讯
Your AI agent writes migrations that look safe. Here's what they actually do to Postgres.
You've seen the headlines by now. An agent in Cursor wiped a company's production database, backups and all, in about nine seconds. Replit's agent nuked another company's prod. Same shape every time: the agent was sure of itself, the SQL was valid, and nobody was in the loop to say wait. Those are the loud failures. The fix for them is boring and you already know it. Don't hand an agent write access to prod. Read-only by default, propose instead of apply, keep a human on the button. But there's a quieter version that a permissions policy won't catch, and that's the one I want to talk about. Your agent is probably doing it right now. It looks completely fine in the diff. The migration that passes review and still takes the site down Ask an agent to make an email column unique. It writes: ALTER TABLE users ADD CONSTRAINT users_email_unique UNIQUE ( email ); Correct SQL. Does exactly what you asked. It sails through review because there's nothing to see. Then on a users table with any real size, it grabs an ACCESS EXCLUSIVE lock and scans every row to build the unique index, and for the whole length of that scan nothing else can read or write the table. The API starts timing out. The connection pool fills. Now you're in an incident over a one-line migration that everybody approved. The agent didn't do anything a decent junior engineer wouldn't have done. That's the trap. The danger isn't the SQL, it's the lock the SQL takes, and you can't see a lock by reading a statement. You'd have to know Postgres locking cold: which DDL grabs which lock, and for how long, and what it shuts out while it holds. And you'll still miss one at 2am. I got tired of missing them. So I measured one. What the lock actually costs I ran the same schema change two ways against a real Postgres 18. Fifty million rows, twenty connections doing ordinary traffic. The unsafe version was a plain SET NOT NULL , which also scans under ACCESS EXCLUSIVE . The safe version was the NOT VALID then VALIDATE da
AI 资讯
I Built a Notebook for Sharing Notes That Doesn't Ask You to Sign Up First
Someone asked me to share meeting notes in Slack yesterday. I pasted the markdown. Slack ate the table. The code block lost its indentation. The task list rendered as literal [ ] characters. I spent five minutes reformatting something that already looked perfect in my editor. So I sent a link instead. Not to Notion — they would have to sign up. Not to Google Docs — same problem, plus I did not want this living in someone's Drive forever. Not to a pastebin — those are single blobs of text with no structure, no pages, no way to come back and fix a typo tomorrow. I wanted: one link, multiple pages, markdown that actually renders, no account required, and a way for me to edit it later without the URL changing. I have built sharing tools before. FreeShare for files. NotePage for single-page text. SharePad is what I wished existed when I needed something in between a pastebin and a wiki — but without the signup wall. Live here: https://sharepad.in Source: https://github.com/Varshithvhegde/sharepad SharePad — Share notes with one link, no signup Write a notebook of markdown pages and share it with a single link. Password lock, expiry dates, comments and PDF export. Free, and no account needed. sharepad.in The Idea Most "share your notes" products follow the same playbook. Create an account. Verify your email. Create a workspace. Invite people. Configure permissions. By the time you are done, the meeting is over and nobody cares about the notes anymore. SharePad skips all of that. You write markdown. You get two links: View link — /n/kitchen-reno — hand this out freely Edit link — /e/{secret-token} — this is your ownership credential. Do not share it. No account. No password to create (unless you want to lock the view link). No "upgrade to share with more people." The edit token is your identity for that notebook. That one decision — token-based ownership instead of user accounts — simplified everything else. No auth flows. No session management. No "forgot password" for th
AI 资讯
2026-08-12 - 1 - ProForma - Guards
Hello I'm Marlene and I invite you to follow my journey developing ProForma.net. But since this is my first post about ProForma, I will give you an overview of what I'm trying to achieve. What ProForma.net is planned to be The main Goal is to develop an application shell for schema based Applications. You will have mainly to different types of UI schemes, first for the Window Layout, there you will tell which elements are contained in the different application sections, like what Buttons or Menus you will have in the window title bar, or what sidebar tabs you will provide for the Ribbons, what the content area is filled with (spoiler I'm going to use flexlayout-react https://github.com/caplin/FlexLayout ). As host application I will write a C# application using the WebView2 abstraction library Photino ( https://www.tryphotino.io/ ). What you can expect In this dev diary series I'll show what I was working on, I'll show you some code and will explain why did to choose the way I did it, or will share some thoughts about the project or the architecture. I also will show you how to write plugins for ProForma, because I plan to handle everything as a plugin so you can change the most aspects of the app. The journey begins: overcome the guard Ok, most of you will know it... parameter checking on top of a method... nearly endless 'if throw' constructs... they are ugly... if (! Directory . Exists ( physicalPath )) throw new DirectoryNotFoundException ( $"Could not find the given path ' { physicalPath } '." ); if ( _directories . ContainsKey ( urlPrefix )) throw new Exception ( $"Key ' { urlPrefix } ' already exists." ); if ( _directories . ContainsValue ( physicalPath )) throw new Exception ( $"Physical Path ' { physicalPath } ' already exists." ); I mean who wants to read that? I don't. So I wanted guards, and I've could used some 3rd Party library, but instead I came up with my own solution for the Guards, since I don't always want to throw the exception on a failed asser
开发者
Netflix Adopts Cloud-Native Job Queueing System Kueue to Replace an In-House Solution
Netflix migrated most of its batch workloads onto Kueue, an open-source cloud-native batch job execution system that has outgrown its homegrown solution over the years. The company mapped the capabilities previously created in-house to Kueue’s functionality and also benefited from new features that would have been costly to incorporate into its homegrown solution. By Rafał Gancarz
开发者
Google’s Pixel Watch 5 Can Now Detect Breathing Emergencies
The company is also adding blood pressure and insulin resistance trends as proactive monitoring tools in the Google Health app.
开发者
Google Pixel 11 Series, Pixel Watch 5, Pixel Tag: Specs, Features, Price, Release Date
The Pixel 11 smartphone lineup is here, alongside the Pixel Watch 5 and the all-new Pixel Tag.
开发者
4 New Camera Tricks on Google’s Latest Pixel 11 Smartphones
From Magic Capture and Instant Night Sight to a built-in teleprompter, here’s a look at a few camera features on Google’s new Pixel 11 series.
AI 资讯
Capacitor Live Updates: Signing vs Encryption
If you compare live update solutions for long enough, you will run into a security claim that sounds decisive: "end-to-end encrypted." It suggests that solutions offering encryption are more secure than solutions that "only" sign their updates. That framing mixes up what the individual security controls in an update pipeline actually do. In this post, we walk through the threat model of live updates (also known as OTA updates or CodePush): what HTTPS already protects, what code signing guarantees, what encryption adds on top, and which of these properties matter for your app. By the end, you can evaluate the security of any live update solution based on facts instead of buzzwords. Key Takeaways HTTPS protects update bundles in transit. It does not protect against a compromised update service, storage bucket, or CDN. Code signing with a developer-held private key guarantees authenticity and integrity all the way to the device: even a fully compromised update infrastructure cannot inject code into your app. Encrypting bundles adds confidentiality only. It provides no additional protection against malicious updates. Client-side encryption cannot keep app code secret, because the decryption key must ship inside the app binary. The React Native maintainers state it plainly: "Code on the client is not secret." If bundles must stay confidential, for example in privately distributed enterprise apps, self-hosting them is a stronger control than encrypting them. The Trust Chain of a Live Update Every live update passes through the same chain: you build a web bundle in your CI/CD pipeline, upload it to an update service, the service stores and serves it (usually through a CDN), and the Live Update SDK in your app downloads and installs it. Security along this chain means three different properties: Authenticity : The update genuinely comes from you. Integrity : The update was not modified on the way. Confidentiality : No third party can read the update's content. For code that
产品设计
NETO: Chat P2P local para equipos dev — sin nube, sin excusas
¿Tu equipo comparte credenciales por Slack? ¿Discuten arquitectura en plataformas que almacenan cada mensaje en servidores ajenos? Existe una alternativa que no depende de la nube: NETO . ¿Qué es NETO? NETO es un chat peer-to-peer diseñado para redes locales . No hay servidor central, no hay cuentas, no hay datos saliendo de tu oficina. Abres el navegador, y los compañeros de tu LAN aparecen automáticamente gracias a mDNS (Multicast DNS), el mismo protocolo que usa Bonjour para descubrir impresoras y servicios locales. Sin registro. Sin configuración. Sin fricción. Cifrado de extremo a extremo real Cada conexión entre peers se establece mediante WebRTC , creando canales de datos directos entre navegadores. Antes de intercambiar un solo mensaje, NETO realiza un intercambio de claves con X25519 (Curve25519 en
AI 资讯
Your publish pipeline is green. Nobody can install your plugin.
Silent failure For three weeks nobody could install your plugin. The publish job was green every single day. You find out when a user asks why the version is so old. Three weeks green, zero installs This happened to us. The publish job for our JetBrains plugin reported success on every run since the middle of July. The plugin was not in the marketplace at all. The registry API answered with a 404. A search for the product name returned nothing. Meanwhile the build was green, the release notes were written, and the changelog was up to date. Nobody noticed. Not the pipeline, not the dashboard, not us. The gap between the last good release and the discovery was three weeks. Your pipeline is not lying to you This is the part worth understanding, because it is why the same thing is probably waiting in your repository too. A release pipeline has one job: get the artifact somewhere a stranger can install it. Almost every pipeline checks something else. It checks that the upload command exited zero. Those two questions agree nearly always. Our publish step went further and did the sensible thing. It caught the failure, compared the error text against a list of known-harmless cases, and exited zero for those. One of those cases was pending moderation. A new version sits in review before it becomes visible. Failing the build for that would be noise, so it was allowed through. Here is the trap. A harmless transient state and a permanent block produce the same message. Once the plugin was stuck, every later run matched the same friendly pattern and reported success. The pipeline answered its question correctly. It was the wrong question. The check that catches it, in about two minutes Ask the store, not the pipeline. That is the whole idea, and you can add it today without changing anything else. One: after publishing, fetch the public listing the way a stranger would. No credentials, no internal API, no authenticated client. Seeing what an outsider sees is the entire point. Tw
AI 资讯
My Comment-Reply Pipeline Picks One Winner Per Thread. Two Commenters Broke That.
reply_comments.py is the script that tells me which DEV.to comments still need a reply. It walks every comment tree on every article I've published and reports the ones I haven't answered yet. I've fixed two bugs in it already: needs_reply() used to think a thread was "handled" forever after a single reply, even if the other person followed up again, and a dedup check was keyed on the thread's root comment instead of whichever message actually needed the reply, so a second round of conversation went permanently invisible. Both fixes are in --selftest now, and both looked, from the outside, like they'd covered this file's tree-walking logic pretty thoroughly. They hadn't. Today I found a third bug in the same handful of functions, and it survives even with both prior fixes applied. What the existing code assumes Comments on DEV.to come back from the API as trees. A top-level comment has a children list, and each child can have children of its own. The function that decides whether a thread needs attention is needs_reply() , built on latest_message() : def latest_message ( comment ): """ The most recently created message anywhere in this comment ' s subtree. """ latest = comment for c in comment [ " children " ]: candidate = latest_message ( c ) if candidate [ " created_at " ] > latest [ " created_at " ]: latest = candidate return latest def needs_reply ( comment ): return latest_message ( comment )[ " user " ][ " username " ] != ME This walks the whole subtree and returns exactly one message: whichever one has the latest timestamp, anywhere in the tree. _pending_entry() (the function pending() actually calls) is built directly on top of that single answer — it checks whether the latest message needs a reply, and if so, returns one entry for the whole thread. That's a reasonable design if a thread only ever grows one message at a time: root comment, my reply, their follow-up, my reply, and so on. Every test case in this file's --selftest , and both of the earlier bug
产品设计
Honor’s Robot Phone Has a Gimbal-Powered Camera for Vloggers
Honor’s new China-exclusive Android phone features a pop-out motorized gimbal camera designed to replace your action cam.
AI 资讯
Honor’s Robot Phone is better than a gimbal in a phone has any right to be
The Robot Phone, which launches today in China, doesn't sound like a good idea - and not only because it's not actually a robot. Instead it's a regular Android smartphone with its main camera installed on a compact gimbal arm that unfolds from the phone's rear. Cramming the equivalent of a DJI Osmo Pocket inside […]