AI 资讯
I Deliberately Destroyed My Kubernetes Cluster at 2 AM. Here's What Died First.
I Deliberately Destroyed My Kubernetes Cluster at 2 AM. Here's What Died First. Chaos engineering is not about breaking things. It's about discovering that your "production-grade" homelab is held together by hope and a single etcd snapshot before someone else finds out for you. The Setup I was lying in bed at 1:47 AM, staring at the ceiling, unable to sleep. Not because of caffeine. Because of a thought that had been gnawing at me for weeks: If one of my nodes died right now, would my cluster actually survive? I run a 4-node bare-metal Kubernetes cluster on Talos Linux. Dell OptiPlex control plane. Three Raspberry Pi workers. Cilium eBPF. ArgoCD. Longhorn distributed storage. Prometheus. Grafana. The whole cloud-native stack, shoehorned into $220 of scrap hardware and stubbornness. From the outside, it looks solid. ArgoCD syncs green. Cilium status shows healthy. Longhorn volumes are replicated across three nodes. I have etcd snapshots every 6 hours to S3. On paper, I'm resilient. But I had never actually tested it. Not a controlled test. Not a graceful node drain. I mean chaos . Sudden death. The kind of failure that happens at 3 AM when a power supply dies, or a kernel panics, or a neighbor's construction crew hits the wrong breaker. So I got out of bed, walked to my desk, and installed Chaos Mesh. Why Chaos Engineering on a Homelab? Professionally, I design AWS infrastructure with multi-AZ failover, auto-scaling groups, and managed services that abstract failure away. At Siemens, if an EKS node dies, the managed node group replaces it before I finish reading the alert. But my homelab has no managed control plane. No AWS SLA. No auto-repair. If a Pi's USB boot drive corrupts, that node is gone until I physically fix it. I needed to know: What dies first when a worker vanishes? Not "what should die" — what actually dies. Does Longhorn really failover? Three replicas sound great until you realize two of them were on the same node. Does Cilium handle network partitio
AI 资讯
AI Incident Copilot Guide for GCC Operations
🚀 Technical Briefing: This tutorial is part of our deep-dive series on Agentic Workflows at Gate of AI . For the full technical breakdown, interactive code sandbox, and the native Arabic translation, visit the original article here . <p>Tutorial</p> <h1>Design a Safer AI Incident Copilot for GCC Operations</h1> <p>An AI incident copilot can help an operations team turn approved engineering facts into a clearer draft for stakeholders. It should not be treated as an autonomous incident commander, a source of truth, or an automatic publishing system. This tutorial explains how to define a safe operating model before choosing a framework, model provider, deployment platform, or integration.</p> <h2>Why incident copilots need a security-first design</h2> <p>During an incident, teams work under pressure. They need to communicate what is happening, who may be affected, what mitigation is under way, and when the next update will arrive. These messages must be accurate, calm, and consistent. An AI assistant may help prepare a first draft, but it can also amplify mistakes if it is allowed to infer missing facts, read untrusted material, or publish messages without review.</p> <p>The available security research on Copilot-style systems is a direct reason to design cautiously. Researchers have demonstrated ways AI systems can be manipulated to provide false references to files, extract some private data, and bypass security protections. The same research describes proof-of-concept abuse that can turn an AI assistant into an automated spear-phishing mechanism after an attacker gains the necessary access. These are not minor quality issues. They show that an AI feature connected to organizational information can become a security boundary.</p> <p>For an incident copilot, the safest initial scope is deliberately narrow: accept a small set of verified facts supplied by an authorized incident lead, create a draft in a fixed communication format, and require a human to review and pub
AI 资讯
Sandboxed Code Evaluation for AI-Generated Outputs — How I Built SafeCode Arena
The Problem: Candidate Code Without Trust You're using Cursor, Claude Code, or GitHub Copilot. The AI gives you three implementation options for the same feature. AI: "Here are three approaches: A) Quick but uses unsafe B) Slower but memory-safe C) Balanced tradeoffs" You: "Which one should I ship?" AI: "It depends..." That "it depends" is where responsibility falls through the cracks. Tests tell you if code compiles and passes specs. But they don't tell you about security, performance, maintainability, or resource limits — all at once. You end up making the call by gut feel. This essay is about building a system that doesn't let that happen. The Solution: Multi-Axis Scoring I built SafeCode Arena — an automated verifier that evaluates code candidates across five axes simultaneously, scores each, and surfaces the tradeoffs. The Five Axes Axis Weight Computation Correctness 50% compile (40%) + tests (40%) + property tests (20%) Security 20% unsafe heuristics (50%) + clippy warnings (50%) Performance 15% relative compile+test time across candidates Maintainability 10% function-length heuristics (60%) + clippy (40%) Resource Usage 5% pass/fail of sandboxed Wasm execution Why These Five? Correctness dominates — code that doesn't work is valueless, so it's 50% Security is explicit — unsafe compiles fine, but you need to detect it yourself Performance and maintainability matter equally — a fast mess vs. a slow masterpiece aren't comparable Resource limits are real — a 100-point algorithm that consumes 2GB is a fail in production Example Scorecard Candidate A: 85 points ├─ correctness: 100 (all tests pass) ├─ security: 60 (2 unsafe blocks flagged) ├─ performance: 70 (10% slower than B) ├─ maintainability: 85 (avg function 25 lines) └─ resource_usage: 80 (Wasm sandbox: 512MB, OK) Candidate B: 92 points ✓ Recommended ├─ correctness: 95 (1 edge case warning) ├─ security: 95 (no unsafe) ├─ performance: 95 (fastest) ├─ maintainability: 88 (avg function 20 lines) └─ resource_usa
AI 资讯
This robot vacuum solves my kitchen stool problem
Robot vacuums are great at keeping your floors clean, but there are a few areas they fall down on the job - stairs being one. Another is chairs and stools. You know the scenario: You have a row of stools at a kitchen table, or chairs tucked under a dining table, and the robovac just […]
开发者
State Management in Front-end Web Development: Mutators
Libraries like Valtio and Pinia for Vue use a mutator pattern instead of the actions, dispatch, and...
AI 资讯
Namecheap closes every auction at 11:00 AM ET. Last-second bidding is a myth.
If you have ever tried to win a domain at auction, you probably assumed the game works like eBay: watch the clock, wait for the last eight seconds, fire your bid, walk away with the name. On Namecheap, that does not work. Not "works badly". Does not work. Namecheap's expiring and marketplace auctions close in a daily batch at 11:00 AM ET. Every auction ending that day ends at roughly the same moment, which means there is no quiet corner of the day where you and one other bidder are paying attention. And if a bid lands in the closing window, the auction extends. So the buzzer-beater you were planning gets absorbed and the clock keeps running. The winner is not the fastest click. The winner is whoever set the smartest proxy maximum, on a name they found before anyone else was looking at it. I have been building PounceDomains around that one fact for months, and it is the reason the product looks the way it does. The edge moved from timing to discovery If speed is not the lever, the levers left are: find the good names earlier, and know what they are actually worth before you commit a number. So the engine scans the Namecheap aftermarket around the clock rather than at the bell. You describe the domains you want in plain English, something like "pronounceable 5-letter .com brandables under $50, no numbers or hyphens", and it builds a tuned config you can edit. If your config is too broad, it tells you and tightens it. There are seven scoring lenses you can stack: pronounceable, brandable, exact-match keyword, short premium, dictionary word, two-word combo, and free-text custom criteria. Fast programmatic filters run first, then AI scores what survives, and only domains that clear your threshold become matches. It has graded over 340,000 domains so far. The second lever is the one I care about more. Every match arrives with its receipts The failure mode in domain investing is not missing a name. It is paying $400 for something worth $80 because a free appraisal tool pri
AI 资讯
DNS Troubleshooting with dig: The Commands DevOps Engineers Actually Need
A surprising share of "the app is down" pages resolve to a name-resolution problem, not a broken service. The service is fine; the client can't turn a name into an address. dig is the precision tool for proving that in seconds instead of guessing. Think about it as a resolution chain, not "is DNS broken" When a name fails, work the chain: which resolver did the client ask, what did that resolver return, and does it match what authoritative DNS actually says? Most incidents live in the gap between those three. The method is boring and reliable: observe the symptom, form a hypothesis about where in the chain it breaks, test with one query, read the evidence, fix, then validate. The single most important habit: query the name from the same host and the same resolver the app uses. Running dig from your laptop proves nothing about what the pod or VM sees. The record types worth knowing You don't need all of them, but you need to recognize them: A / AAAA — name to IPv4 / IPv6 address. The usual suspect. CNAME — an alias pointing at another name. A stale or wrong CNAME sends traffic somewhere unexpected. MX — mail routing. TXT — SPF, DKIM, domain verification, and other metadata. NS — which servers are authoritative for a zone. SOA — the zone's serial and TTL defaults; the serial tells you whether a change has propagated. PTR — reverse lookup, IP back to name. The commands that actually earn their place Start with the quick answer, then get precise. dig +short api.internal.example.com +short strips everything except the answer. If it prints an IP, resolution works from this host. If it prints nothing, you have a real failure to chase. Empty output is a signal, not an error. dig api.internal.example.com A The full form. Read the status in the header: NOERROR with an ANSWER section is good; NXDOMAIN means the name genuinely doesn't exist; SERVFAIL points at a broken upstream or DNSSEC issue. Also note which SERVER answered at the bottom — that's the resolver you're actually
AI 资讯
GitHub API Rate Limits: an Unauthenticated 304 Still Costs You a Request
No token. One IP. July 29, 2026: GET /repos/python/cpython 200 5996 B remaining 32 -> 31 + If-None-Match (no Authorization header) 304 0 B remaining 31 -> 30 + If-None-Match 304 0 B remaining 30 -> 29 + If-None-Match 304 0 B remaining 29 -> 28 Three conditional requests. Three 304 Not Modified . Zero bytes of body across all three. Three requests gone from a bucket of 60 per hour. I opened the terminal to write the opposite post. The short version: if you call the GitHub REST API without an Authorization header, an If-None-Match request that comes back 304 still decrements x-ratelimit-remaining . The ETag saves you bytes. It does not save you quota. GitHub's documentation states the claim five times on one page and attaches the condition to two of them, and that clause falls off easily when a sentence gets quoted on its own. The post I meant to write My working title was something like "poll GitHub for free with ETags". I believed it. I had read the sentence about 304 responses not using your rate limit, I had repeated it to other people, and the plan was a tidy little piece with a before-and-after budget chart. The first run killed it. remaining went down. My first reaction was that my counter reading was wrong, which is the normal reaction and usually the correct one. It was not wrong. So the post changed, and the finding turned out to be worth more than the one I went in with. Does a 304 count against the GitHub rate limit? What the docs actually say Here is the part that matters, and I want to be precise because it would be easy and dishonest to turn this into "GitHub's docs are wrong". They are not. On the page Best practices for using the REST API the claim shows up five times. Two of the five carry a condition; three do not. Here is the strict one, the only place on the page where the condition is spelled out as a header: "Making a conditional request does not count against your primary rate limit if a 304 response is returned and the request was made while c
AI 资讯
I'm building Guren, a fullstack TypeScript framework for the AI-agent era
Guren is a fullstack TypeScript framework for Bun. I started it because I wanted Laravel's shape in TypeScript, and I kept going for a different reason: once I was handing most of the code to agents, what I wanted from a framework was a way to check what came back. gurenjs / guren Guren is a Bun-native TypeScript MVC framework that unites Laravel-like ergonomics with Hono, Inertia.js, React, and Drizzle ORM, aiming to deliver a fast, elegant full-stack workflow that keeps frontend and backend work in sync. Guren The fullstack TypeScript framework for the AI-agent era. Laravel-style conventions, end-to-end type safety, and built-in agent introspection and verification — routing, controllers, ORM, authentication, and Inertia.js + React in one cohesive experience that humans and AI coding agents navigate from the same map. v2 — Stable. Breaking changes only in major releases, per the release policy . Quick Start # 1. Scaffold a new app with authentication (dependencies install automatically) bunx create-guren-app my-app --auth cd my-app # 2. Run migrations and seed the demo user (SQLite by default — no server needed) bun run db:migrate bun run db:seed # 3. Start the dev server bun run dev Open http://localhost:3333 and sign in at /login with demo@example.com / secret . Add features as you go bunx guren add auth # Authentication bunx guren add resource posts --fields " title:string,body:text " # CRUD resource bunx guren add queue # Background jobs … View on GitHub I like the way Laravel and Rails let you build. A feature is a route, a controller, a model and a view, and authentication, queues, mail and validation are already wired together before you start. TypeScript has the parts. Hono for HTTP, Drizzle for the ORM, Zod for validation, Inertia and React for rendering, all of them good. What's missing is an agreed way to connect them, so every project ends up wiring it slightly differently, and I've written that wiring more times than I want to count. The mistakes move
AI 资讯
I Built a 40-Minute Evaluation for Free Model Endpoints. Here's the Scorecard.
Free model endpoints are seductive. Zero cost. Zero setup. Zero reason to trust them. I don't trust demos. I trust failure modes. So I built a small evaluation harness. It tests one thing: can a free model endpoint gate a pull request for secrets? This is not a benchmark. It's a repeatable experiment. You can run it in an afternoon. Disclosure: This article was prepared as part of MonkeyCode's product outreach. I used MonkeyCode's free model endpoint and the free server option for the test. No quotas. No hardware claims. Just a harness and a rubric. Why I stopped trusting free endpoints Free endpoints look great in a demo. You paste a diff. The model finds the secret. Everyone claps. Then you wire it into CI. The JSON breaks. The latency spikes. The model misses a private key. The demo didn't show that. An evaluation will. The experiment I designed a 40-minute test. It answers one question: where does the free endpoint perform well, and where does it break? The dataset is 30 synthetic diffs. Fifteen contain real-looking secrets. Fifteen are clean. Each diff is small. Each diff has one clear change. The prompt is strict. The model must return JSON. No prose. No apologies. Just a verdict. # eval_secret_gate.py # Simplified harness. Adapt to your client SDK. import json , time def classify ( client , diff : str ) -> dict : prompt = f """ You are a secret scanner for code review. Return ONLY JSON with this shape: {{ " contains_secret " : true, " line " : 12, " type " : " aws_access_key " }} Diff: { diff } """ start = time . time () response = client . complete ( prompt , model = " free " , server = " free " , # free server option ) latency = time . time () - start return { " latency " : latency , " raw " : response } def evaluate ( client , diffs , runs = 3 ): for i , diff in enumerate ( diffs ): for run in range ( runs ): yield i , run , classify ( client , diff ) The harness is deliberately small. It measures five things. Accuracy. JSON validity. Latency. Variance. Fa
AI 资讯
Opinion: The Diff Is a Claim, the Probe Is the Proof
Opinion: The Diff Is a Claim, the Probe Is the Proof A generated patch is a claim about how a system should behave, and a diff cannot verify that claim on its own. The only honest reviewer is the runtime itself, which means every AI-proposed change deserves a behavioral probe before a human spends attention on it. Free model access changes the economics of that review, because the verification loop no longer costs a developer's full attention or a paid compute budget. The practical implication is that a disposable server, such as the free server option in MonkeyCode, becomes the arbiter of whether a patch is even worth reading. Disclosure: This article was prepared as part of MonkeyCode's product outreach. Review sessions routinely burn forty minutes on a diff that a five-second HTTP probe would have rejected instantly, and that waste is now entirely avoidable. Why Line-by-Line Review Fails on AI Patches A human reviewer reads a diff as prose, searching for the author's intent, but an AI-generated patch has no reliable intent to recover. The model that wrote the change cannot explain why a specific flag was flipped, and the diff itself only records the surface edit. This is a fundamental mismatch between the review tool and the review question. The review question is not "what changed" but "does the system still behave correctly after this change." Runtime shape diffing answers the first question well, and I have argued before that shape is a useful gate, but shape alone misses semantic regressions. A service can keep the same endpoints, the same config keys, and the same file layout while silently returning wrong data. Behavioral probes close that gap because they test the contract between the service and its callers. A probe sends real requests, checks real responses, and records real state transitions, which is exactly the evidence a reviewer needs. This is why I take the position that the probe, not the diff, should be the primary review artifact. Treat Every Pa
开发者
We Bought a $500 Counterfeit Rolex So Good, Even Rolex Didn’t Spot It
The replica watch industry is in its “super clone” era. Following tips from murky internet forums, we bought three budget fakes that were good enough to pass as real—but ultimately disposable.
产品设计
There’s a Very Simple Reason Why You Love the Slate Truck
The car world is filled with designers still obsessed with shoehorning sex into their rides. Slate went a different route, and it’s why people are smitten with the charming electric pickup.
AI 资讯
Rules, Standards, and a Missing Line on My Chart
Hello, I'm Maneshwar. I'm building git-lrc, a Micro AI code reviewer that runs on every commit. It is...
AI 资讯
The Login Loop of Doom.
This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry . Code snippets are recreated and anonymized for illustrative purposes. The Symptom: A Revolving Door Instead of a Login Page It started innocently enough: I was clicking through our app and hit "Log in." Auth0's Universal Login page appeared, I entered my credentials, got redirected back to the app... and landed on the Auth0 login page again. And again. And again. No error message. No failed login attempt. Auth0 was happily authenticating me every single time — and our app was just as happily bouncing me right back, like a bouncer who checks your ID, nods, and then immediately forgets he checked it. The login loop. Every developer's favorite horror movie, now starring me. Red Herring #1: "It's the Frontend's Fault" My first suspect was the obvious one: the frontend callback handler. A Node.js/Express app sits in front of our Django API, handling the Auth0 redirect dance. A login loop screams "broken callback" or "state/nonce mismatch," so I spent a solid hour there: ✅ State parameter matched ✅ Nonce validated ✅ Callback URL whitelisted in the Auth0 dashboard ✅ ID token and access token both present in the response Everything the frontend touched was perfect. The tokens were real, signed by Auth0, freshly issued seconds ago. And yet the moment the frontend sent the access token to our Django API, the API answered with a flat 401 Unauthorized . Fine. New suspect. Red Herring #2: "Auth0 Must Be Misconfigured" Next stop: the Auth0 dashboard. Maybe the token lifetime was set to something absurd, like 5 seconds? Maybe the audience claim was wrong? Token lifetime: 3600 seconds. Normal. aud claim: matched our API identifier exactly. Signature: verified against the JWKS. Valid. So Auth0 was issuing perfectly good tokens, the frontend was delivering them intact, and Django was spitting them out. The bug had to be in the validation logic itself. Time to actually read the code we trusted blindly e
AI 资讯
What If the Blockchain Could Judge Your Bluff Without Seeing Your Dice?
Liar’s Dice sounds like a perfect game to put onchain. The rules are simple, every move can be verified, and you don’t need a centralized game server deciding who won. There is just one problem. Blockchains are public. Liar’s Dice only works if your dice are private. If I simply stored every roll inside a normal smart contract, anyone could inspect the state and know exactly what everyone was holding. At that point, there is no bluffing. You would basically be playing poker with everyone's cards face up. So I built FHE Liar’s Dice , a decentralized version of the game where your dice remain encrypted while the game is being played. Not hidden behind a backend. Not stored privately in some database. Encrypted onchain. And the interesting part is that the smart contract can still use those encrypted dice to determine whether you are lying. The problem with putting hidden-information games onchain Most blockchain games actually benefit from transparency. If you're building something like chess, every player is supposed to know the complete state of the board. Liar’s Dice is different. Each player starts with five dice that only they should be able to see. Players then make public claims about the combined dice across the entire table. You might say: There are six 4s on the table. The next player has two choices. Raise the bid. Or call your bluff. The entire game comes from the fact that nobody knows exactly what everyone else is holding. But a traditional smart contract has the opposite property. Its state is transparent. Even if the frontend refuses to display your dice, someone can simply inspect the contract, query the state, watch events, or build their own interface. Hiding something in the UI isn't privacy. I needed the actual game state itself to remain secret. FHE turned out to be a very good fit for the game I built the game using Fhenix CoFHE . Fully Homomorphic Encryption is interesting because it allows computation to happen directly over encrypted values.
AI 资讯
React useScrollLock Hook: Lock Body Scroll for Modals (2026)
Your modal is open, centered, perfect. Then someone flicks the overlay and the page behind it scrolls away underneath. Everyone's first fix is the same three lines: useEffect (() => { document . body . style . overflow = open ? " hidden " : "" ; }, [ open ]); It works on your laptop. Then the bug reports arrive: On iPhone the page still moves. iOS Safari rubber-band scrolls the document by touch even with overflow: hidden on <body> . Something else got wiped. "" isn't necessarily what was there before — you just erased whatever your design system or CSS-in-JS had set inline. Two overlays, one frozen page. A drawer and a lightbox both own body.style.overflow ; close them in the wrong order and the page never scrolls again. The layout jumps the instant the desktop scrollbar disappears. useScrollLock from @reactuses/core is those three lines with the hard parts handled: it restores the exact inline overflow it replaced, adds a touchmove guard on iOS that still lets your modal's own content scroll, exposes the lock as React state you can render off, and works on any element — not just <body> . This post covers what it actually does line by line, why overflow: hidden is not enough on iOS, how it compares to the position: fixed and body:has(dialog[open]) approaches, and the six gotchas that show up in real apps. Quick Start npm install @reactuses/core import { useScrollLock } from " @reactuses/core " ; import { useEffect } from " react " ; function Modal ({ open , onClose , children }: ModalProps ) { // a getter, not `document.body` — see the SSR gotcha below const [, setLocked ] = useScrollLock (() => document . body ); useEffect (() => { setLocked ( open ); return () => setLocked ( false ); // release even if we unmount while open }, [ open , setLocked ]); if ( ! open ) return null ; return ( < div className = "overlay" onClick = { onClose } > < div className = "sheet" onClick = { e => e . stopPropagation () } > { children } </ div > </ div > ); } The signature: const [
AI 资讯
Multi Agent Collaboration Gets Persistent Compute in Bedrock AgentCore
Amazon Web Services has extended Amazon Bedrock AgentCore with runtime instances, a new compute option that gives AI agents persistent infrastructure purpose-built for complex long-running workflows and multi-agent coordination. By Matt Saunders
AI 资讯
Why pasted text keeps breaking search and formatting (and the regexes I ended up using to clean it)
I kept running into a boring problem that was harder to debug than it should have been: text that looked normal, but behaved wrong the moment I pasted it into a CMS, a spreadsheet, or a code comment. Search would fail. Line breaks would get weird. A heading copied from ChatGPT would drag Markdown markers along with it. Sometimes the only visible clue was that the punctuation felt slightly "off." What finally made this manageable wasn't some big NLP trick. It was going back to the dumb, reliable layer: exact character matching. The tool I built for this is basically a pile of small, deterministic cleanups for the specific junk that copied text tends to accumulate — full-width punctuation mixed into ASCII, invisible Unicode code points, curly quotes, em dashes, leftover Markdown, and whitespace noise. The most useful part is the invisible-character scan, not the cleaning The piece I trust most in the whole component is the part that explicitly names which invisible characters it cares about, then counts them by code point. It's not doing a vague "this text seems suspicious" pass. It has a hard-coded inventory: const invisibleDefs = [ { key : " zwsp " , codes : [ 0x200b ] }, { key : " zwnj " , codes : [ 0x200c ] }, { key : " zwj " , codes : [ 0x200d ] }, { key : " bomZwnbsp " , codes : [ 0xfeff ] }, { key : " wordJoiner " , codes : [ 0x2060 ] }, { key : " softHyphen " , codes : [ 0x00ad ] }, { key : " bidiMarks " , codes : [ 0x200e , 0x200f , 0x202a , 0x202b , 0x202c , 0x202d , 0x202e ] }, ]; const codesToRegex = ( codes ) => new RegExp ( `[ ${ codes . map (( c ) => " \\ u " + c . toString ( 16 ). padStart ( 4 , " 0 " )). join ( "" )} ]` , " g " ); const analyzeInvisible = ( str ) => { const breakdown = invisibleDefs . map (( def ) => ({ key : def . key , count : ( str . match ( codesToRegex ( def . codes )) || []). length , })); const total = breakdown . reduce (( sum , row ) => sum + row . count , 0 ); return { breakdown , total }; }; I like this because it's brutall
AI 资讯
I was tired of clunky PGP tools, so i built my own cross-platform solution: PGP Manager
I work with PGP regularly and I work with it across multiple operating systems. Linux on my workstation, a MacBook on the go and every now and then I have to touch Windows. And on every single one of them, PGP means a different tool: Kleopatra on Linux, GPG Keychain on Mac, Gpg4win on Windows (which is Kleopatra again, just wrapped differently). Three tools, three UIs, three sets of quirks, three different workflows and none of them are what I'd call user-friendly. And yes, i know: the GnuPG CLI is the same everywhere, and it's a great tool. I use it. But gpg --encrypt --sign --armor -r test@key.com is not something I want to type 100 times a day and it's definitely not something I can give to a non-technical colleague. Every time I had to walk someone through encrypting or decrypting a message, I lost a bit of hope. So I made it my mission to finally build something better: PGP Manager . A free and open-source desktop app that looks and works the same on Linux, Mac and Windows. Why another PGP Tool? The cryptography behind OpenPGP is mature and has been trusted for decades. The problem was never the crypto, it's the workflow and the fragmentation. Encrypting a message for a colleague shouldn't require different tools per OS and a wiki page. My goal was simple: all the everyday PGP tasks in one place, without dumbing anything down or inventing a new format. PGP Manager is not a new crypto system. It uses gopenpgp v3 (ProtonMail's OpenPGP library) and standard OpenPGP (RFC 4880), so it stays fully compatible with GPG, Kleopatra, Thunderbird and the rest. You can leave anytime, your keys are just standard armored files. One more thing that sets it apart from the tools above: they're all frontends for a local GnuPG installation. PGP Manager brings its own OpenPGP implementation, so there's nothing else to install. It can still read an existing GnuPG keyring if you have one, but it doesn't need it. That's also what makes the standalone/USB mode possible in the first pla