今日已更新 249 条资讯 | 累计 37498 条内容
关于我们

标签:#Web

找到 2730 篇相关文章

AI 资讯

What Your Multisig Threshold Actually Protects

I've been digging into multisig configurations for bridge and protocol security reviews. The threshold gets all the attention — 3-of-5, 4-of-7, whatever. But after checking a few dozen Safes on mainnet, the threshold is rarely the weakest link. There are five other things that determine whether a Gnosis Safe actually protects funds, and most people only check the first one. This post walks through all of them, with cast commands you can run yourself. What the threshold does The threshold sets the minimum number of owner signatures required to execute a transaction through execTransaction() . If threshold is 3 and you have 2 signatures, the call reverts. Simple. # check threshold and owners cast call <SAFE> "getThreshold()(uint256)" cast call <SAFE> "getOwners()(address[])" This is the part everyone understlse. What the threshold does NOT protect 1. Modules This is the biggest blind spot in multisig security. Safe modules are contracts authorFromModule()`. A module can execute*any transaction from the safe without a single owner signature*. The threshold is irrelevant. The module has its own authority. `bash if this returns anything other than an empty array, investigate cast call "getModulesPagin[],address)" \ 0x0000000000000000000000000000000000000001 10 ` Modules are legitimate — timelockation. But a malicious or compromisedmodule is a full bypass of every threshold. Your 7-of-10 means nothing if a module can move funds independently. 2. Guard A guard contract implements checerExecution() . It adds validation on top of the threshold — restricting destinations, limiting values, blocking certain operations. The guard address lives at a specific storage slot. If it's 0x00 , there's no guard. No additional checks beyond threshold + signatu `bash guard storage slot (keccak256("guard_manager.guard.address")) cast storage \ 0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8 0x000...000 = no guard installe ` A guard can enforce things like "no transfers ab

2026-08-22 原文 →
AI 资讯

My first website said "Don't commit without context." I never committed it at all.

The renewal notice came and I decided to let it go. threadkeeper.io was my first idea and my first website. I bought the domain in August 2025, about six weeks after a community college AI summer camp where I was writing files with names like ccc-ai-pdf-project and describing them in my own README as a beginner Python project. Then I shipped a domain, a blog, a CLI, and a manifesto. Before I let it lapse I went back to look at it one more time. Sentimental. Five minutes, tops. Then I tried to figure out where the source code lived, and realized it did not live anywhere. The site was on Spaceship. I had built it there, in the browser, and never put it in version control. Not once. There was no repo to clone, no local folder, no backup. The only copy of my first website that existed in the world was the one running on a server I had four days left on. The tagline on that site, in cyan, at the top of the page, was "Don't commit without context." I never committed it at all. I did not have the source code to my own website So the first job was not nostalgia. It was extraction. I pulled all eight pages and every asset off the live server before it went dark: the landing page, the blog, three posts, the Dr. Kahlo page, and the Ariadne Clew recap app I built for an AWS hackathon. Nineteen files. sitemap.xml claimed there were four pages, which tells you how much I trusted my own sitemap in 2025. The rest I found by following links. That archive is now public, with a SHA-256 for every original file so anyone can verify nothing drifted in the rescue: earlgreyhot1701d.github.io/threadkeeper-archive It is committed now. A year late. I named a file dom_js.js and did not blink Here is the first thing I found once I could actually read my own code. The Ariadne Clew app had seven JavaScript modules. Two of them were named with snake case and a suffix: api_js.js , dom_js.js , main_js.js . Four were camelCase with no suffix: utils.js , theme.js , exportMarkdown.js , dragDrop.js . Tw

2026-08-22 原文 →
AI 资讯

A rule-based Korean dialect converter with no LLM: eight guards that keep standard Korean intact

I built a Korean dialect converter that runs entirely in the browser and uses no LLM. Type a standard Korean sentence and it rewrites it the way people speak in Gyeongsang, Jeolla, Chungcheong, Gangwon or Jeju. Paste a dialect sentence and it converts back to standard Korean. Nothing leaves the browser. Try it: https://toolnjoy.com/dialect-converter (Korean UI) The hard part was not producing dialect. It was not breaking standard Korean . Every time I widened a rule, a perfectly normal sentence got mangled somewhere. Here is what I ended up with. Most of it applies to any rule-based text rewriter (honorific converters, profanity filters, spelling normalizers). Shape of the thing Four modules: file job core sentence splitting, sentence-type detection, ending replacement endings per-region ending tables (declarative / interrogative / propositive / imperative × politeness) words per-region vocabulary reverse dialect → standard Forward conversion goes "decide the sentence type, then pick the region's ending". Reverse goes the other way: "look at the ending, infer the sentence type". You cannot just invert the forward table, so reverse has its own table. Each reverse rule carries a type , which removes the detection step entirely. In reverse mode the user does not pick a region. I run all five and keep the one with the highest score (4 points per matching ending + syllable count of matched words). People who paste dialect usually do not know which region it is from. That is why they came. Eight guards, each from a real failure 1. Never reverse a form that appears on the standard side of any table. The Gyeongsang table had 함께 → 같이 (together). Reverse flipped it and rewrote "같이 보게" into "함께 보게", except 같이 is perfectly standard. Fix: anything that ever appears in a standard column anywhere is excluded from reverse candidates. 2. Question endings only count when there is a question mark or a wh-word. Gyeongsang -나 / -노 mark questions. But 하나 ("one") also ends in -나 . Without

2026-08-22 原文 →
AI 资讯

How the Nostr journey is going for me?

A few years ago, I discovered the Nostr protocol 1 thanks to Jack (Jack Dorsey) 2 . I've been using it actively ever since. I already had an interest in distributed social networks for years. I tried ActivityPub 3 for a while, but something just didn't click. Around that time, Jack started ATProto 4 within Twitter, but something felt off there too. I hadn't even bothered trying the methods that came before those anyway. It always bothered me—and many others, of course—that the internet became so centralized when the web is distributed by nature. The main reason for this was social media platforms. The trend that started with guestbooks evolved into forums, then to Friendfeed-like walls, MySpace-like platforms, and eventually Facebook and Twitter. The internet, which had a much more distributed structure with blogs prior to Facebook and Twitter, suddenly started to centralize. Due to the maintenance costs of distributed structures and unsettled systems, humanity took the easy way out and chose centralization, as always. Facebook and Twitter grew steadily, and many other platforms alongside them transformed into centralized hubs using the exact same playbook. One of the things people realized too late was that centralized power always possesses the ability to manipulate, a fact that eventually came to light through the Facebook and Twitter lawsuits and similar cases over time. Moreover, there was no such thing as account ownership in any of these centralized systems; we were essentially no different from non-paying tenants (or tenants paying with our data). As time passed, we saw what happened when the landlords started evicting people. Nowadays, even just posting anti-government content can get you kicked out of these centralized platforms in an instant, and you have absolutely no ownership rights over your data or anything else. We've actually been living in a crappy system for a long time, and this system is still going on. From the era I caught onto, ActivityPub w

2026-08-21 原文 →
AI 资讯

The duration your video API accepts is not the duration it renders

A sequence I cut to a music bed was three frames out at the first transition, nine at the second, and by the sixth segment nothing lined up with anything. I had asked every generation for ten seconds. Every generation had returned a file that was not ten seconds. Nothing in the API said so. The request took duration: 10 , returned 200 , and produced an MP4 whose container duration was 8.708 . No warning field, no note in the response body, and — the part that actually cost me the afternoon — no mention of it on the docs page I had read three times. This is a general property of latent video models rather than a bug in one provider, and once you know the shape of it you can handle it in about twenty lines. Here is the shape. Seconds are the wrong unit A video diffusion model does not work on frames. It works on a compressed latent tensor, and the compression is temporal as well as spatial: a causal 3D autoencoder folds a run of input frames into a single latent frame. Because the encoder is causal, the first frame is kept whole and everything after it is compressed in groups. With a temporal stride of s , a clip of F frames becomes latent_frames = ( F - 1 ) / s + 1 which only divides evenly when F ≡ 1 (mod s) . Frame counts that miss that condition get padded or truncated, so implementations pick the nearest legal count and render that instead. Stack a second constraint on top — many of these models generate in fixed blocks of latent frames rather than one at a time — and the set of renderable lengths collapses into a short arithmetic progression: F = head + block · n n ∈ ℕ Every legal duration is one of those F values divided by the frame rate. Nothing between them is reachable. duration: 10 is not a request. It is a hint that gets snapped to a grid you were never shown. What the snapping does to users Three separate problems, and only the first is obvious. The output is not the length you promised. Your UI said 10s, the file is 8.708s, so your UI lied. Not by much,

2026-08-21 原文 →
AI 资讯

Why I Built a No-Signup QR & URL Utility Platform (And How to Use the API)

We've all been there: a client needs a quick QR code for a print campaign, or a short link for social media. You search Google, click the first result, and realize you need to create an account, verify your email, and potentially pay after 14 days. To solve this friction, we built klick.tools . It's a collection of simple web tools that just work - no signup, no expiration dates, and full GDPR compliance. What's inside? QR code generator: 7 content types (URL, text, WiFi, vCard, email, phone, geo), custom colors and module shapes, automatic WCAG contrast check, and clean export as PNG, SVG or PDF. Rendering happens in the browser, so the payload never leaves the device. URL shortener: shorten a link in seconds, see click stats, and change the target later without reprinting anything. The developer API We didn't want to build just another consumer site, so everything is backed by a REST API. Base URL: https://klick.tools/api/v1 . Responses are always JSON - lists as { data, count } , writes as { message, data } , errors as { error } with a stable machine-readable code . Creating a short link is a single POST, and it works without an account at all (rate-limited per IP): curl -X POST https://klick.tools/api/v1/links \ -H "Content-Type: application/json" \ -d '{"targetUrl": "https://example.com/a-very-long-campaign-url"}' With an API key ( kt_live_... , generated in your account) the link is bound to you, so you get click counts, editing and higher quotas. Pass it as a Bearer token or via x-api-key : const res = await fetch ( " https://klick.tools/api/v1/links " , { method : " POST " , headers : { " Content-Type " : " application/json " , Authorization : `Bearer ${ process . env . KLICK_TOOLS_API_KEY } ` , }, body : JSON . stringify ({ targetUrl : " https://example.com/landing " , title : " Summer campaign " , }), }); const { data } = await res . json (); console . log ( data . shortUrl , data . clickCount ); The same pattern covers QR codes via /api/v1/qr - create, li

2026-08-21 原文 →
AI 资讯

How I run a full AWS-powered website for less than $1/month

Most developers assume running a real web platform on AWS costs a fortune. Mine doesn't. HomeServerLab — a free AWS learning platform with an AI assistant, tutorials, OAuth login, and an Apps Marketplace — runs for less than $1/month in infrastructure costs. Here's exactly how. The stack and what it costs AWS Lambda Every route on the site is handled by a single Lambda function written in Python. No EC2, no always-on server, no idle costs. Lambda charges per invocation and per GB-second of compute — at my traffic levels, it stays well within the free tier and costs virtually nothing beyond it. API Gateway HTTP API The entry point for all requests. HTTP API is significantly cheaper than REST API on AWS — $1 per million requests. At my current traffic, this rounds to zero. DynamoDB Handles rate limiting, user sessions, chat history (with TTL), and OAuth state. On-demand pricing means I pay per read/write, not for provisioned capacity. Again, within free tier at my scale. Amazon Bedrock (Nova Micro) Powers the built-in AI assistant. Nova Micro is one of the cheapest foundation models available on Bedrock — and with a 25 messages/day rate limit per user, costs stay negligible. Cloudflare (Free plan) Sits in front of everything: DNS and proxying, CDN and caching (reduces Lambda invocations), WAF and bot protection, DDoS mitigation, and Cloudflare R2 for the Apps Marketplace (10GB free tier). All of this on the free plan — $0/month. CloudFront (Free tier) Sits between Cloudflare and Lambda as an additional layer. 1TB of data transfer and 10 million requests/month free. More than enough. The real cost The only real cost is Bedrock — and even that is minimal with rate limiting in place. Everything else stays within free tiers at my traffic levels. Total: less than $1/month. The key insight Serverless means you pay for what you use, not for what you reserve. Combined with Cloudflare's free plan absorbing most of the traffic before it even hits AWS, the actual billable usage

2026-08-21 原文 →
AI 资讯

Powerful regression tests for your PostgreSQL project

Mark (aka Winsaucerer) here to show you how you can test your PostgreSQL database like a sorcerer. We are going to be using Spawn, a SQL build system supporting migrations and testing. You do not need to be using Spawn for migrations in order to use it for testing. Spawn does not require any extension installed. All you need is the spawn CLI and a psql connection to the database for Spawn to connect through. Spawn was built to solve some migration pains I've experienced, but I happily discovered that when used for testing, it is very powerful. To show you some of that power, we're going to use a contrived database example. It uses golden file testing to determine success. When the test runs, we capture the stdout and stderr output from psql, and compare that to expected output. Testing with Spawn involves these steps: Create a new test with spawn test new <name> and fill out the test steps Check test outputs with spawn test run <name> (or view the SQL that will be sent to psql via spawn test build <name> ) When outputs are as expected, create the golden file with spawn test expect <name> Run the test and compare to expected output with spawn test compare <name> For now, Spawn only supports connecting via psql, which means that you have access to all the features that psql provides. To get started, follow the Spawn install instructions: Install Spawn And then create a new folder on your system, and initialise a new project with a docker compose config ready for us to play with: # inside your new folder: spawn init --docker docker compose up -d You now have a running docker based PostgreSQL database and a spawn.toml file configured to connect to it. We are not assuming that you are using Spawn or any other tool for migrations, so you can manually create and update the database by connecting directly using psql: docker exec -ti postgres-db psql -U postgres Create the database ⚠️ Caution This post is not intended as an example of how to build an orders database. The des

2026-08-21 原文 →
AI 资讯

Code Smell 321 - Getter Piggybacking

One broken window invites another TL;DR: Don't reuse an existing getter to bolt on new business logic from outside the object. Problems 😔 Duplicated business rules Broken encapsulation Scattered comparison logic Hidden domain knowledge Fragile refactoring Law of Demeter violation Solutions 😃 Add real behavior methods Keep comparisons inside object Pass collaborators, not primitives Reserve getters for rendering Follow tell, don't ask Refactorings ⚙️ Refactoring 027 - Remove Getters Maxi Contieri Maxi Contieri Maxi Contieri Follow Apr 18 '25 Refactoring 027 - Remove Getters # webdev # programming # beginners # java 3 reactions Add Comment 17 min read Refactoring 013 - Remove Repeated Code Maxi Contieri Maxi Contieri Maxi Contieri Follow Jun 16 '24 Refactoring 013 - Remove Repeated Code # webdev # beginners # programming # tutorial 2 reactions Add Comment 3 min read Context 💬 An object exposes a getter for one legitimate reason: some other part of the system needs to read that value, usually to display it. Getters are a code smell, but this one gets a pass, for now. Later on, you discover that you need new business logic that depends on the same value. You already have the getter, so you write a function outside the object that calls it and does the comparison itself, breaking the encapsulation principle. Someone else needs slightly different logic based on the same value. They also call the getter and write their own version of the comparison. Now two places decide what that value means , and neither of them is the object that owns it. Typical. You didn't add a second getter this time. You reused the first one, because it was already there. That's the trap. The getter existed for one reason, and you let it justify skipping the real fix: a method on the object that answers the question itself, instead of handing out the raw value for every caller to interpret on their own. Sample Code 💻 Wrong 🚫 // Food needs to show its use-by date on the shelf // label, so useByDate(

2026-08-21 原文 →
AI 资讯

Detecting Tool + Schema Drift in a Remote MCP Server

An MCP server can ship a change that breaks every agent calling it, and nothing in your monitoring will notice. The endpoint still answers 200. The initialize handshake still completes. tools/list still returns a result. Every signal a conventional uptime check knows how to read stays green — and an agent that memorized last week's tool contract starts failing anyway, because the contract underneath it moved. This is drift: a server's tool inventory or a tool's input schema changing between two points in time, with no transport-level symptom at all. Drift is not a hypothetical. MCP servers are young, most are maintained by small teams, and a tool's inputSchema is just a JSON object in a deploy — there is no compiler stopping someone from renaming a required field, tightening an enum, or dropping a tool nobody remembered an agent still called. The only way to catch it is to have looked at the server before and remember what you saw. What Actually Counts as Drift Drift is anything about a server's advertised capability contract that differs from the last time you checked. Concretely: A tool disappears. It was in yesterday's tools/list , it is not in today's. Any agent that calls it now gets a JSON-RPC error mid-flight, not at startup — the failure shows up wherever the agent happens to reach for that tool. A tool appears. Informational on its own, but worth recording — it is also how you notice a server quietly forking its capability set per client or per deploy. A tool's contract changes shape. Same name, different inputSchema — a field renamed, a type narrowed, a new required parameter, a changed description that alters how an LLM decides to call it. The tool is still callable, which is what makes this the dangerous case: nothing errors immediately, calls just start failing validation or getting silently misinterpreted. The capability set changes. The server stops advertising resources or prompts , or starts. Anything built against the old capability list breaks the

2026-08-21 原文 →
AI 资讯

Your feature-usage scanner doesn't know Vue, Svelte, or Astro exist. Here's how we fixed that without touching its core.

If a static analyzer only walks .ts / .tsx / .js / .jsx , every other file type isn't scanned badly - it's not scanned at all. A .vue component, a .svelte widget, an .astro page: none of them exist to the tool. Not "low confidence." Not "partial support." Invisible, the same way an empty search result looks identical whether there's genuinely nothing to find or the search just never looked in the right place. That's exactly the gap Eventra's CLI had. It scans a codebase and tells you which tracked features are actually used - the whole pitch is "stop guessing which code is dead." Except if your team ships a Vue admin panel, a Svelte checkout widget, and an Astro marketing site around the same core app (which, if you've worked on more than one team, you've probably seen - nobody plans a multi-framework stack, it just accretes), the CLI would silently skip all three, report a clean scan, and never mention that it hadn't actually looked. The exact failure mode the product exists to prevent, happening inside the product itself. We'd already closed this gap once, for Vue. This month we closed it for Svelte and Astro too, and the interesting part isn't the frameworks - it's that adding two more meant touching exactly zero lines of the CLI's core analysis engine. The trick: don't teach the core anything The CLI's core is a TypeScript-compiler-API engine: it builds a real program, walks real ASTs, resolves real symbols across files, and figures out which .track("event_name") calls are statically reachable. It is, deliberately, framework-agnostic - it doesn't know what Vue is, and it shouldn't have to. So instead of teaching the core about .vue / .svelte / .astro , each framework gets a small, separate plugin whose only job is a translation: take the framework file, hand back one virtual TypeScript module. A Vue Checkout.vue becomes Checkout.vue.ts . A Svelte Cart.svelte becomes Cart.svelte.ts . The core never sees the original file - it sees TypeScript, because by the time

2026-08-21 原文 →
AI 资讯

Google Trends API: the 200 OK that means you got soft-blocked

Google Trends has no public API. What it has is the same internal JSON endpoints the trends.google.com single-page app calls — and those endpoints do something most REST clients aren't built to survive: they answer with HTTP 200 and an empty body when Google decides you look like a bot. Quick answer A 200 OK from Google Trends' widgetdata endpoints does not mean you got data. If the response body is empty, Google soft-blocked the request without bothering to send a 429. The fix is to stop trusting the status code alone: check resp.text.strip() on every call that's supposed to return a payload, and if it's empty, rotate the proxy session and retry exactly like you would on a 429 — because that's what it functionally is. if status == 200 : if require_body and not resp . text . strip (): # Soft block: Google returns 200 with empty body when it detects bots. # Treat the same as 429 — rotate session and retry. logger . warning ( " %s: HTTP 200 but empty body (soft block, attempt %d/%d) " , ...) if proxy_cfg is not None : new_sid = _fresh_session_id () current_proxy_url = await proxy_cfg . new_url ( session_id = new_sid ) await asyncio . sleep ( delay ) continue return resp Why does a working response start with )]}' ? Every Trends JSON endpoint prepends an XSSI-protection prefix before the actual JSON body — a defence against cross-site script inclusion attacks that predates fetch() . Naive json.loads(resp.text) throws a JSONDecodeError on a perfectly healthy response. Worse, the prefix isn't even consistent: /trends/api/explore sends )]}'\n (no comma), some widgetdata endpoints send )]}',\n (with a comma). We check the longer variant first so a response using the short prefix doesn't get mis-stripped: XSSI_PREFIX = " )]} ' , \n " XSSI_PREFIX_NO_COMMA = " )]} ' \n " def _strip_xssi_prefix ( body : str ) -> str : if body . startswith ( XSSI_PREFIX ): return body [ len ( XSSI_PREFIX ):] if body . startswith ( XSSI_PREFIX_NO_COMMA ): return body [ len ( XSSI_PREFIX_NO_COMMA

2026-08-21 原文 →
AI 资讯

A CSS Hover-Reveal Pattern for Technical Specs

The problem on the Gate Seal page The Gate Seal product page for a maritime client needed to present detailed specifications without turning the layout into a wall of text or a table that looked like an export from Excel. The technical detail buyers cared about was present, but visually buried. The requirement was to surface those details in a compact way, keep the implementation CSS-only, and make sure it still worked with keyboard navigation. The hover-reveal pattern The pattern below uses a hover-reveal on key specification rows. On desktop, moving the cursor over a spec row reveals additional context. With a keyboard, focusing the same row does the same thing. No JavaScript is required for the basic interaction. Structurally, each spec item is a container with two layers of content: Always-visible summary (label and primary value) Hidden detail that appears on hover or focus Here is a simplified version of the markup: <div class="spec-list"> <button class="spec-item"> <div class="spec-main"> <span class="spec-label">Gate size</span> <span class="spec-value">Up to 6 m</span> </div> <div class="spec-detail"> Custom diameters available for retrofit situations. </div> </button> <button class="spec-item"> <div class="spec-main"> <span class="spec-label">Seal material</span> <span class="spec-value">EPDM / NBR</span> </div> <div class="spec-detail"> Oil-resistant compounds for lock gates in heavy traffic.</div> </button> </div> The choice of <button> here is deliberate: it is naturally focusable, works with keyboard navigation, and is announced as an interactive element by assistive technology. In a production implementation, the button semantics can be adapted depending on whether you need a true button or a different element with role="button" . The CSS-only interaction The interaction is controlled through :hover and :focus-visible , with a basic transition for a smoother reveal. .spec-list { display: grid; gap: 0.75rem; } .spec-item { width: 100%; text-align: left

2026-08-21 原文 →
AI 资讯

The Hard Part of Birth Chart Calculations Isn't the Zodiac. It's Time.

The most annoying bugs I’ve dealt with while building a birth-chart engine were not about zodiac signs. They were about time. And the deeper I got into it, the more I realized that “birth time” is a much less simple input than it looks on a form. A local datetime isn't enough Take this: 1990-05-15 09:30 It looks precise. But precise where? Without a timezone, it doesn’t identify an instant. So the calculation API I use takes both the local datetime and the IANA timezone: chart = engine . natal ( local_datetime = " 1990-05-15T09:30:00 " , timezone = " Europe/London " , latitude = 51.5074 , longitude =- 0.1278 , ) I prefer an IANA zone like Europe/London over something like UTC+1 . The former describes a real timezone with historical rules. The latter is just an offset. DST makes this more interesting During a daylight-saving fall-back transition, the same local clock time can occur twice. So a value like: 01:30 may correspond to two different UTC instants. That means the input looks exact to the user while still being ambiguous to the calculation engine. There’s a strong temptation to quietly choose one. I don’t like doing that. If the input is genuinely ambiguous, I’d rather make the ambiguity explicit. The same thing happens in the opposite direction during spring transitions. Some local clock times never existed. If the clock jumped directly from 01:59 to 03:00, then: 02:30 isn’t a valid local instant. Again, silently “fixing” it is convenient. But now the software has changed the user’s data. Then there’s the bigger problem: no birth time A lot of people simply don’t know what time they were born. This creates a product decision. You can say: unknown → 12:00 PM and suddenly everything works. You get: Ascendant houses house cusps Midheaven The object looks complete. But none of those values are based on a birth time the person actually supplied. That bothered me enough that I made unknown birth time a first-class state in the engine. chart = engine . natal ( local

2026-08-21 原文 →
AI 资讯

The Hidden Reasons Your iOS App Feels Slow

An iOS app can feel slow even when its interface looks well-designed and responsive. The problem may not always be the UI or the code running on the device. Often, the real issues are hidden in network requests, API responses, WebSocket connections, and background activity. For developers, finding these problems requires visibility into what is happening behind the screen. This is where Owlse , a network inspection and debugging tool for iOS and macOS developers, can help. 1. What Actually Makes an iOS App Feel Slow? Several hidden network issues can affect an app's performance: Slow API responses Too many network requests Large data payloads Connection delays Failed or repeated requests Background network activity A user may simply see a loading screen or delayed response, while several network operations are happening in the background. Understanding these operations is the first step toward finding the actual cause of the problem. 2. Why Traditional Debugging Can Make These Issues Hard to Find Network-related problems are not always easy to identify through standard debugging. Developers may need to switch between different tools to inspect requests, analyze timing, investigate WebSockets, and understand application issues. When an app generates hundreds of requests, finding one problematic request can also take considerable time. Without a clear view of network activity, developers often have to rely on assumptions. A dedicated network debugging workflow can make this process much easier. 3. Meet Owlse: Network Debugging for iOS & macOS Owlse is built to give iOS and macOS developers greater visibility into their application's network activity. Instead of treating network behavior as something happening in the background, Owlse helps developers inspect and understand it. With features including live request streaming, request inspection, timing analysis, WebSocket inspection, mocking, crash reporting, search, HAR export, and timeline debugging, Owlse brings impo

2026-08-21 原文 →
AI 资讯

AI Content Labels: Build Trust Signals Before Users Stop Believing the Page

The web is entering an awkward phase: users can still read everything, but they cannot easily tell what they are reading. A support answer, product review, help article, sales email, synthetic image, and generated video can all look polished enough to pass at a glance. That is useful for builders. It is also a trust problem. Recent signals point in the same direction. Pew Research Center analyzed roughly 490,000 English-language webpages from Common Crawl and found that 10% of sampled pages showed significant signs of AI authorship. For pages published after ChatGPT launched, the share rose to more than one-third. Google has added API-level disclosure support for AI-generated or edited advertising assets. C2PA and Content Credentials are becoming normal terms in media provenance discussions. For developers building AI products, the lesson is simple: content labeling is no longer a policy footnote. It is becoming product infrastructure. This guide shows how to build AI content labels that are useful, honest, and developer-friendly without turning your app into a wall of legal text. Why AI content labels matter A label is not just a badge that says “made with AI.” A useful label answers the reader’s next trust question: Was this generated, edited, summarized, translated, or only checked? Was it reviewed by a person? Which source data did it use? Is the claim verified or just model-generated? Has the content changed since approval? Can the user inspect more detail if they need it? This matters because AI content sits in different risk zones. A playful image caption and a generated billing-policy answer should not receive the same treatment. A model-written changelog and an AI-edited medical explanation do not carry the same stakes. The common mistake is treating disclosure as one boolean: { "ai_generated" : true } That field is better than nothing, but it is too flat. It does not explain what happened. It does not help support teams investigate mistakes. It does not te

2026-08-21 原文 →
AI 资讯

The Principle of Least Privilege: Why File Permissions Like 600/644/755 Exist

Anyone who has worked with SSH private keys has run into an instruction to "set it to 600." Config files, by contrast, often get 644, and executable scripts get 755. What do these three-digit numbers actually mean, and why does the right number depend on what kind of file you're dealing with? This post starts from the mechanics of Unix-style (Mac/Linux) file permissions and works up to the design principle behind them: least privilege. Permissions as a 2D grid of who and what Unix-family operating systems express file access as a grid: three kinds of "who" crossed with three kinds of "what." "Who" breaks down into the file's owner, the group the owner belongs to, and everyone else ("other"). "What" breaks down into read, write, and execute. Each cell in that 3×3 grid is either granted or not, and that's exactly what a listing like -rw-r--r-- from ls -l is showing you. Strip the leading character and the remaining nine characters are three groups of three — owner, group, other — each rendered as r/w/x when granted or - when not. Why a single digit can represent read/write/execute Numeric notation like chmod 600 compresses that rwx combination into a single octal digit. Read is worth 4, write is worth 2, execute is worth 1 — powers of two — and you sum whichever bits are set. Note: powers of two are used here because each of read/write/execute is tracked as an independent bit (on or off), and any sum of a subset of {4, 2, 1} maps back to exactly one combination of bits. There's no ambiguity — for example, 6 can only mean read+write (4+2), never any other combination. Read and write, no execute ( rw- ): 4 + 2 = 6 Read only ( r-- ): 4 Read, write, and execute ( rwx ): 4 + 2 + 1 = 7 No access at all ( --- ): 0 A three-digit number like 600 lines up these single digits for owner, group, and other, left to right. 600 means "owner gets read+write, group and other get nothing." What the common numbers actually mean Reading the numbers mentioned at the top through this lens:

2026-08-21 原文 →
AI 资讯

The Active Flag Trap: unvalidated-but-logged-in in CakeDC/Users

If you ship email validation with CakeDC/Users , you eventually hit a question the plugin quietly hands back to you: what should happen when someone registers, never clicks the validation link, and then tries to log in? The honest answer is that CakeDC/Users doesn't decide for you. Out of the box you get a database column, a couple of behaviors, and a set of events — but the experience is yours to assemble. Get it wrong and you land in one of two bad places: a user silently logged in without ever validating, or a user who typed the right password and is told "username or password is incorrect." Neither is what you want. This post walks through why that happens in v16, and a clean way to wire the flow using the events the plugin already dispatches — no core hacks, no schema surgery. One flag, two meanings Everything starts with a single boolean column on the users table: active . When email validation is on, registration creates the account with active = 0 and only flips it to 1 when the user clicks the link in the validation email. You can trace it in BaseTokenBehavior::_updateActive() : // $user['validated'] is a transient flag set to false during register() $emailValidated = $user [ 'validated' ]; if ( ! $emailValidated && $validateEmail ) { $user [ 'active' ] = false ; // registered → inactive + token emailed $user -> updateToken ( $tokenExpiration ); } else { $user [ 'active' ] = true ; // clicked the link → active $user [ 'activation_date' ] = new DateTime (); } Notice there is no separate validated column in the database — $user['validated'] is a transient property used only during registration. The persisted truth is active , and it is doing two jobs at once: "Has this person confirmed their email?" — set by the validation flow. "Is this account enabled?" — the thing an admin toggles to ban or suspend someone. That conflation is the root of everything below. Hold onto it; we'll come back to it. How the finder decides who exists Login in CakeDC/Users runs thro

2026-08-21 原文 →
AI 资讯

Why I Built a Zero-Knowledge, Client-Side Encrypted Burning Note App Over the Weekend

Hey everyone! 👋 Like many developers and sysadmins, I constantly find myself needing to share temporary credentials, API keys, or sensitive text with clients and coworkers. Dropping these straight into Slack, Discord, or standard email always feels like a massive security headache because those chat platforms store everything in plain text in their databases. I looked into popular "one-time secret" web utilities, but I noticed a major flaw: almost all of them handle the encryption and decryption on their servers. That means you have to blindly trust their backend configurations, logging policies, and database security. I wanted something truly zero-knowledge where the server owner physically couldn't read the notes even if they wanted to. So, I built ScorchNote : https://scorchnote.com 🛠️ How it Works (Under the Hood) To achieve absolute zero-knowledge, ScorchNote relies on strict client-side mechanics: Browser-Side Encryption: When you type a secret, the data is encrypted directly in your browser before it ever leaves your network interface. The URL Hash Advantage: The decryption key is generated and stored inside the URL's hash fragment (everything after the # ). Zero Server Footprint: Web browsers never send the hash fragment to the host server during HTTP requests. This means my database only receives a completely scrambled, encrypted payload. The server has no concept of what the key is. Millisecond Burn-on-Read: The moment the recipient visits the link, the encrypted payload is fetched and instantly purged from the server database. 🚀 Try It Out I kept the page entirely lightweight, minimalist, and completely free of bloated tracking scripts. It’s built to do exactly one job, safely and instantly. I would love to hear your thoughts on the architecture, the user experience, or what features you think I should cook up next! Check it out here: ScorchNote

2026-08-21 原文 →