AI 资讯
Idempotency Keys: Designing APIs That Survive Retries
Every API that sits behind an unreliable network eventually faces the same problem: a client sends a request, the connection drops before the response arrives, and the client has no idea whether the operation happened. Did the payment go through? Did the order get created twice? The client's only safe move is to retry — which means your server needs a story for what happens when the same "create this thing" request arrives more than once. That story is idempotency keys, and getting the details right is more subtle than it first looks. The core idea The client generates a unique token — typically a UUID — once per logical operation, and attaches it to every retry of that operation: POST /orders Idempotency-Key: 7c3fd9a2-df01-4b3e-9a55-1e5f9b6b6d55 {"sku": "WIDGET-1", "qty": 2} The server's job is to guarantee that no matter how many times a request with that key arrives, the side effect (charging a card, creating an order, sending an email) happens at most once, and every retry gets back the same response the original request would have produced. Note what this is not: it is not deduplicating by request body. Two requests with identical bodies but no key are legitimately two different orders for two widgets. The key is what marks them as "the same attempt," not the payload. The naive approach, and why it breaks A common first pass is a table like: CREATE TABLE idempotency_keys ( key TEXT PRIMARY KEY , response_body JSONB , status_code INT ); On each request: check if the key exists, and if so return the cached response; otherwise do the work and insert the result. This looks right and is wrong in a specific way: it has a race condition. Two retries can arrive concurrently (a client that timed out and fired a second attempt while the first was still in flight), both miss the cache check, and both execute the underlying operation. You've now charged the card twice. Making the check-and-do atomic The fix is to claim the key before doing the work, using the database's ow
AI 资讯
Absorber les +50 % de l'API Claude sans couper une feature
Le 1er septembre 2026, le tarif de lancement de Claude Sonnet 5 s'arrête. L'input passe de 2 $ à 3 $ le million de tokens, l'output de 10 $ à 15 $ : +50 % sur les deux lignes, pour tout le monde qui appelle l'API en paiement à l'usage. La panique par défaut, c'est de couper des fonctionnalités ou de rétrograder vers un modèle plus faible. Il y a mieux, et c'est déjà dans l'API. Deux mécanismes — le prompt caching et le batch — encaissent la hausse à ta place, souvent avec de la marge. Voici le code, les chiffres, et les pièges que j'ai payés pour que tu ne les paies pas. Ce qui bouge exactement le 1er septembre Trois lignes suffisent à raisonner. Le reste du barème (Opus, Haiku, contexte 1M) ne change pas. Poste Sonnet 5 (par M de tokens) Jusqu'au 31 août Dès le 1er sept. Input standard 2 $ 3 $ Output 10 $ 15 $ Lecture cache (hit) 0,20 $ 0,30 $ Retiens la troisième ligne, parce que c'est elle qui gagne la partie. Un cache hit coûte 10 % du prix d'input . Même après la hausse, lire depuis le cache à 0,30 $ reste moins cher que l'ancien input plein à 2 $. Autrement dit, le contexte que tu répètes à chaque appel — un system prompt costaud, une doc, des exemples few-shot — peut être payé une fois puis relu pour trois fois rien. Le prompt caching, concrètement Le principe est simple : tu marques un bloc stable avec cache_control , et tout ce qui précède ce marqueur est mis en cache. Le premier appel paie une écriture ; les suivants, dans la fenêtre TTL, lisent à 10 %. import anthropic client = anthropic . Anthropic () DOCS = load_docs () # ~20 000 tokens, identiques à chaque requête def ask ( question : str ): return client . messages . create ( model = " claude-sonnet-5 " , max_tokens = 1024 , system = [ { " type " : " text " , " text " : " Assistant support de l ' app Lumière. " }, { " type " : " text " , " text " : DOCS , " cache_control " : { " type " : " ephemeral " }, # TTL 5 min }, ], messages = [{ " role " : " user " , " content " : question }], ) La question de
AI 资讯
Beyond Login: Building a Production Authentication Lifecycle in FastAPI
Authentication is often presented as a short sequence: Accept a username and password. Return a JWT. Protect a few endpoints. That is enough for a tutorial, but it is not an authentication lifecycle. Real applications must also answer harder questions: How is an email address verified without storing a reusable secret? What happens to existing sessions after a password reset? Can a user see and revoke a lost device? How do we prevent a rotated refresh token from being replayed? How should TOTP secrets and recovery codes be stored? How can an OIDC identity be linked without trusting email matching? I explored those questions while building FastAPI Production API v1.2.0 , a backward-compatible authentication lifecycle release for an open-source FastAPI backend foundation. This article explains the design decisions behind it—not just the endpoints that were added. 1. Model lifecycle tokens as scoped, single-use credentials Email verification and password recovery look similar from the outside: send a link, receive a token, and update an account. Treating them as interchangeable, however, creates unnecessary risk. The release uses account-action tokens with four important properties: Random: the token is generated as an opaque secret rather than derived from user data. Scoped: a verification token cannot be used as a password-reset token. Expiring: every token has a short, configurable lifetime. Single use: confirmation atomically marks the token as consumed. Only a hash of the token is persisted. The original value exists only long enough to be delivered to the user. This gives email verification and password reset a shared security primitive without making their policies identical. The main endpoints are: POST /auth/email-verification/request POST /auth/email-verification/confirm POST /auth/password-reset/request POST /auth/password-reset/confirm Both request operations return uniform responses. A caller should not be able to determine whether an email belongs to an a
AI 资讯
I built a tool that catches an active Steam rating decline before it snowballs (and tells you why)
The problem Steam's "Overwhelmingly Positive" badge is an all-time average. It can stay green for weeks after a patch, a pricing change, or a broken launch actually tanks a game's rating. Most devs find out from an angry tweet or a Reddit thread, not from Steam itself. I'd already built a similar review-mining tool for the App Store (a "copy+10%" market-research Actor — different post, different audience). Same week, I wondered: does Steam expose anything as clean as Apple's public RSS/Lookup endpoints? Turns out yes — better, actually. What Steam gives you for free Two public, zero-auth endpoints: https://store.steampowered.com/appreviews/<appid>?json=1&filter=recent https://store.steampowered.com/appreviewhistogram/<appid>?l=english The first gives you individual reviews with voted_up (boolean, no star-rating math needed), playtime_at_review , refunded , written_during_early_access — much richer than I expected. The second gives you a rolling histogram of recommendations_up / recommendations_down per period (weekly for active games, monthly for older ones), which is the actual key to detecting a real decline instead of noise. The bug that mattered I first tried Steam's documented day_range parameter to window the query_summary to "last N days." It doesn't do anything — I tested it against a game with 800K+ reviews spanning two years, day_range=30 and no day_range at all returned byte-identical totals. Not documented as broken anywhere I could find, so noting it here in case it saves someone else the debugging time. The appreviewhistogram endpoint is the actual fix: compare the most recent period's positive % against a baseline of prior periods, with a minimum-volume gate (I use 20 reviews/period) so a slow week on a small indie title doesn't get mistaken for a crisis. Real result Ran it against a game that had a rough launch week. Flagged an active decline immediately, and the negative-review sample broke down as 43% bugs/performance + 18% server issues — i.e., a
安全
Stop using the localStorage hack to sync browser tabs. BroadcastChannel does it natively.
When a user logs out in one tab, the other tabs should follow. When they update their cart, every...
AI 资讯
I built a pricing API for LLMs — then realized the real users might not be human
Klikk her: LLM Price Watch started as a simple problem: comparing per-token pricing across Claude, GPT, Gemini, DeepSeek, and Grok meant opening five pricing pages and doing the math by hand every time a new model dropped. So I built a calculator. Then I built an API behind it. Then I noticed something about who was actually going to call that API. The obvious version The first version of the API was exactly what you'd expect: GET /v1/models — every tracked model with current pricing GET /v1/models/:id — a single model GET /v1/calculate?model=X&input_tokens=N&output_tokens=N — cost for a specific call Straightforward. A human developer hits /calculate , gets a number, builds their cost estimate into a dashboard somewhere. Done. The part that changed the design The actual differentiator turned out to be a fourth endpoint: GET /v1/recommend?use_case=X . Instead of just returning prices, it returns a recommendation — which model fits a given use case (long-document summarization, high-volume classification, coding assistance, customer support) based on both price and the editorial analysis already written for the comparison pages on the site. Once that endpoint existed, the actual audience for this API stopped being "a developer building a cost dashboard" and started including something else: AI agents doing their own tool selection at runtime. An agent framework deciding which model to route a task to doesn't want to read a blog post — it wants a structured answer to "given this use case, what should I use, and what will it cost me." That's a tool call, not a page view. That reframing changed a few concrete decisions: CORS is wide open on purpose. This isn't an API with a dashboard in front of it — it's meant to be called directly from wherever the calling code lives, including client-side agent code. No API key required (for now). Every bit of friction between "an agent wants this data" and "an agent gets this data" is friction against the actual use case. A paid tie
AI 资讯
Which EU countries let you check a company for free: a status table
If you are building anything that touches European business data — onboarding, invoicing, KYB, fraud checks — you will eventually ask the same question I did: which countries can I actually get company data from, for free, without an account? I could not find this written down anywhere, so I worked it out the hard way while building a supplier checker. Here it is. The baseline: VIES The European Commission runs VIES , which validates VAT numbers across all 27 member states plus Northern Ireland ( XI ). It is free, it needs no key, and it is the obvious starting point. Two things about it are worth knowing before you build on it. It answers one question: is this VAT number currently registered. It does not tell you the company is solvent, trading, or that it has not been struck off. A company in liquidation keeps a cleanly resolving VAT number for months, because deregistration and insolvency are run by different authorities on different timetables. Name and address are returned for 25 of the 28 jurisdictions, not all of them. Germany and Spain confirm registration but publish no company name through VIES. I tested three valid numbers for each before accepting that. For those two, a yes/no is genuinely all you can honestly show. Where you can go further, free Ten countries publish enough through a national register to add something meaningful on top of VIES: Country Free register Reports company state Reports VAT-active Romania yes yes yes Poland yes — yes Slovenia yes — yes Estonia yes yes — France yes yes — Greece yes yes — Bulgaria yes yes — Latvia yes yes — Czechia yes — — Finland yes — — Company state means the register tells you whether a business is inactive, in liquidation, bankrupt, insolvent, terminated or struck off. This is the valuable column, and only six countries have it. VAT-active matters more than it sounds. VIES cannot distinguish "this is a real company that is not VAT-registered" from "this number belongs to nobody". Three registers can. Note th
AI 资讯
Why Your ZATCA Phase 2 Invoice Passes Compliance and Fails Reporting
If you are integrating ZATCA Phase 2 (Saudi Arabia's Fatoora e-invoicing) and you have seen this: { "type" : "ERROR" , "code" : "signed-properties-hashing" , "category" : "CERTIFICATE_ERRORS" , "message" : "Invalid signed properties hashing, SignedProperties with id='xadesSignedProperties'" } ...after your invoice sailed through /compliance/invoices , this post is for you. It is the single most confusing failure mode in the whole integration, and the fix is not what the error suggests. The trap: SignedProperties exists in two byte-shapes The XAdES SignedProperties block is referenced twice in your signed document: ds:Reference URI="#xadesSignedProperties" carries a digest of the block. The block itself is embedded inside ds:Object > xades:QualifyingProperties . The natural assumption is that both refer to the same bytes. They do not. The hashed shape carries namespace declarations and starts at column 0: <xades:SignedProperties xmlns:xades= "http://uri.etsi.org/01903/v1.3.2#" Id= "xadesSignedProperties" > <xades:SignedSignatureProperties> <xades:SigningTime> 2026-08-07T02:14:33 </xades:SigningTime> <xades:SigningCertificate> <xades:Cert> <xades:CertDigest> <ds:DigestMethod xmlns:ds= "http://www.w3.org/2000/09/xmldsig#" Algorithm= "http://www.w3.org/2001/04/xmlenc#sha256" /> The embedded shape carries no namespace declarations (they are inherited from ancestors) and its root element is indented to column 32 : <xades:SignedProperties Id= "xadesSignedProperties" > <xades:SignedSignatureProperties> Embed the hashed shape verbatim - the intuitive thing to do - and the gateway rejects with signed-properties-hashing , even though your indentation "looks right". The second half of the trap: the digest encoding The digest is not the raw SHA-256 bytes in base64. It is base64 of the hex string : const crypto = require ( ' crypto ' ); // hashedShape = the namespaced, column-0 variant above const propsDigest = Buffer . from ( crypto . createHash ( ' sha256 ' ). update ( Buffer .
AI 资讯
A graduated response ladder where every rung is invisible
Detection produces a number. Something has to turn that number into a response, and the response has two hard constraints that pull against each other: It must be proportionate . A score of 55 is not a score of 95, and treating them the same means either blocking clients you shouldn't or serving attackers you shouldn't. It must be invisible . An attacker who learns they were detected changes tactics, and you've converted a detection into a training signal for them. The second constraint rules out most of the obvious implementations of the first. The ladder Six tiers, evaluated in Rego, driven by the pushed risk score plus the gateway's own fast-path signals: allow → log → throttle → step_up → deny → revoke The policy is small enough to read in full, and mirrors the scorer's thresholds exactly: score_tier := "deny" if { score_fresh ; score_entry . score >= 85 } score_tier := "step_up" if { score_fresh ; score_entry . score >= 70 ; score_entry . score < 85 } score_tier := "throttle" if { score_fresh ; score_entry . score >= 50 ; score_entry . score < 70 } score_tier := "log" if { score_fresh ; score_entry . score >= 30 ; score_entry . score < 50 } final_tier := fast_tier if { tier_rank [ fast_tier ] >= tier_rank [ score_tier ] } final_tier := score_tier if { tier_rank [ score_tier ] > tier_rank [ fast_tier ] } The final decision is the more severe of two independent signals: the scorer's composite, and the gateway's own sub-second guardrails. The second exists because windowed scoring cannot react faster than one window, and a flood needs stopping before then. Fail open, deliberately # FAIL-OPEN: when the risk-score data is missing or stale we allow (with a # logged reason) rather than deny. This is the choice most likely to get an argument, so here's the reasoning. This is a detection layer bolted onto a production API. If the scorer, the pipeline, or the data path between them breaks, failing closed takes the real API down for every legitimate integration — a self-i
AI 资讯
Honeytokens that recognise themselves: stateless decoys with automatic attribution
Every signal in the previous articles is statistical. They weigh evidence, they have thresholds, they can be argued with. Honeytokens are different in kind. A honeytoken is a record that does not exist and was never given to anyone . Nothing legitimate can ask for it, because nothing legitimate has ever held a reference to it. A request for one isn't suspicious — it's proof that someone is guessing or working from a stolen list. That makes it the highest-confidence signal available, and worth building carefully. Derivation: make the decoy recognise itself The obvious implementation is a table. Generate decoy IDs, store them, and check every miss against the table. That has two problems. It puts a database lookup in the request path on every miss — and misses are exactly what a flood produces. And it doesn't tell you whose decoy was tripped without another join. Instead, derive them: export function honeytokenFor ( clientId : string , n : number , generation = 0 ): string { const mac = createHmac ( ' sha256 ' , config . honeytokenSecret ) . update ( ` ${ clientId } : ${ generation } : ${ n } ` ) . digest (); return uuidFromBytes ( mac . subarray ( 0 , 16 )); } Three properties fall out of this, and they're the whole design: Recognition is stateless. Given any ID and any client, recompute the client's decoy set and check membership. No lookup, no cache, no round trip. The gateway precomputes each known client's set at startup into a Set and membership is O(1). Attribution is automatic. The client ID is inside the derivation. There is no "which client did this decoy belong to?" question — a decoy for integration-acme is not a decoy for anyone else, and cannot be. If a decoy seeded into acme's scope is requested by a different credential, that's information too. They're format-identical to real IDs. The output is shaped as a v4 UUID — correct version and variant nibbles — so it is indistinguishable from a real documents identifier: export function uuidFromBytes ( bytes
AI 资讯
Azure API Management Adds Dedicated AI Gateway Tier, Governing Models and MCP Tools
Microsoft released a dedicated AI Gateway tier of Azure API Management in public preview, with a control plane built around models, MCP servers and tools rather than APIs. It fronts Foundry, Bedrock, Vertex AI and OpenAI behind one endpoint, with policy cards instead of XML. Architects welcomed the consolidation while questioning where the governance boundary sits. By Steef-Jan Wiggers
AI 资讯
The Silent Costs of AI APIs Nobody Warns You About
I remember the exact moment the excitement turned to dread. I had just integrated GPT-4 into a side project—a small document summarization tool. The pricing page said $0.03 per 1K input tokens and $0.06 per 1K output tokens. Clean, simple, two numbers. I calculated roughly $0.01 per summary and smiled. Two weeks later the bill arrived: $87.43 for what I thought would be maybe $15. I wasn't being careless. I had read the docs. I knew about tokens. But the silent costs—the ones nobody puts in a neat table—had quietly multiplied my burn rate by six. That experience taught me that AI API pricing is a lot like buying a printer. The upfront cost is seductive; the real expense hides in the ink cartridges, the proprietary drivers, and the forced upgrades you never planned for. Let's talk about those hidden costs, because I'll bet you've either already hit them or you're about to. The Token Trap That Isn't What You Think Everyone knows tokens are the unit of billing, but the gap between "understanding tokens" and "feeling tokens" is enormous. First, there's the input/output asymmetry . GPT-4 charges double for output tokens. That's fine for short answers, but what about chain-of-thought? If you ask the model to reason step-by-step, those intermediate steps count as output tokens—and they add up fast. I had a single query balloon from 500 output tokens to 2,400 because the model decided to work through a logic puzzle aloud. My cost quadrupled without me changing a thing in my prompt. Then there's the system prompt tax . Many developers stuff context into system messages: instructions, examples, formatting rules. Those are input tokens paid every single time, even when the user's query is tiny. If your system prompt is 1,500 tokens and you handle 10,000 requests, that's 15 million input tokens you're paying for—whether the model uses them or not. And don't get me started on retry costs . You hit a rate limit or your request times out? The token count for that failed request? S
AI 资讯
🧹 From Urban Gardens to Clean Streets: Building a Decentralized Robot Ecosystem with MyZubster and Monero"
What started as a vision for mapping urban gardens has evolved into something much bigger. Over the past weeks, we've built a complete decentralized ecosystem that connects IoT sensors, robots, and communities using Monero (XMR) and MYZ tokens. The Journey: From Gardens to Streets It all began with a simple idea: create a map for urban gardens. But we quickly realized that a map alone wasn't enough. We needed a full system that could: Monitor soil health in real-time Automate irrigation and analysis Enable private, decentralized payments Connect communities and institutions Here's what we built. 🗺️ The Urban Garden Map Using Leaflet.js and a REST API, we created an interactive map where anyone can register their urban garden. The map supports: Geolocation with /nearby endpoint Full CRUD operations for gardens Search by name and city Check it out: Live Demo 📡 Arduino Sensors for Smart Agriculture We integrated Arduino sensors to monitor soil conditions in real-time: pH (0-14 scale) EC (Electrical Conductivity) Temperature and Humidity The data flows through Node.js APIs and is stored in MongoDB, making it accessible for analysis and reporting. 🦾 The Robot Ecosystem We built a family of software robots that can receive payments automatically in MYZ and XMR: 1. AgricoloBot - The Garden Assistant Monitors soil health Generates automatic reports Provides recommendations for farmers 2. Robot Arm - The Physical Gardener 4 DOF (Degrees of Freedom) Controlled via WebSocket Can water, plant, analyze, and harvest 3. CleanStreetBot - Street Cleaning Robot Reports waste with geolocation Automates zone cleaning Generates reports for municipalities 4. RecicloBot, PuliziaBot, CompostBot - Recycling and Waste Management Monitor containers and optimize collection routes Track composting and organic waste management 💰 Decentralized Payments with Monero and MYZ All robots receive automatic payments through an escrow system: 85% → Robot owner 2% → MyZubster platform 8% → Bosco Community
AI 资讯
Google's Custom Search image API dies in 2027. Two traps in replacing it.
Google's Custom Search JSON API is closed to new customers, and existing customers have until 2027-01-01 to move off it. That deadline takes searchType=image with it. I maintain cse-bridge , a small self-hosted service that speaks Google's customsearch/v1 wire format on top of your own SearXNG instance, so migrating is a base-URL change rather than a rewrite. Web search shipped first. This week I added image search — and it turned out to be much less mechanical than "map some more fields", because two of the assumptions that hold for web results are actively wrong for image results. Both are worth knowing whether or not you ever use my code. If you are writing anything that normalises image search results, you will hit them. Trap 1: link is not the page For a web result, Google's link is the URL of the page. Easy. For an image result, link is the image file itself , and the page it was found on lives in image.contextLink : { "link" : "https://facts.net/wp-content/uploads/2020/08/AdobeStock_209028852.jpeg" , "displayLink" : "facts.net" , "image" : { "contextLink" : "https://facts.net/nature/animals/red-panda-facts" , "thumbnailLink" : "https://ts1.mm.bing.net/th?id=OIP.I_aIcVvl98DbktQmP297ugHaE7&pid=15.1" , "width" : 4000 , "height" : 2666 } } SearXNG has it the other way round: the result's url is the page, and the image is in a separate img_src field ( documented here ). So the naive mapping — reuse the web mapper, add an image object — produces items whose link points at an HTML document. That fails silently , which is what makes it nasty. Your JSON still validates. Your item count is right. Every field is a well-formed URL. But every client that does <img src={item.link}> — which is the entire point of image search — renders nothing, and it looks like the images are broken rather than like your mapper is wrong. The fix is a rule, not a patch: if a result has no image URL, drop the whole result . Never fall back to the page URL to keep the count up. export functio
AI 资讯
Matching 90M+ music tracks across six platforms: ISRCs, fuzzy matching, and what breaks
I run a music metadata API as a solo developer. Under it sits a catalog of 90M+ recordings aggregated from six platforms: Spotify, Apple Music, Tidal, Beatport, Discogs, and MusicBrainz. The core job is cross-referencing: take whatever you know about a track (an ISRC, a platform ID, or a messy "artist + title" string from a DJ export) and resolve it to one canonical recording with everything else attached. When I started, I assumed this was mostly a plumbing problem. Every platform has an API, recordings have a standard identifier, join on it, done. Almost none of that survived contact with real data. This post is the parts I had to learn the hard way: why one song legitimately carries many ISRCs, how fuzzy matching on artist and title actually has to work, why recording-to-composition mapping is many-to-many in both directions, and the failure modes I now check for routinely. The ISRC almost solves it The ISRC (International Standard Recording Code) is a 12-character identifier for a specific recording. Daft Punk's "One More Time" is GBDUW0000053 : country prefix GB , registrant code DUW , year 00 , then a designation number. Every commercially released recording is supposed to have one, and most platforms expose it. So the naive architecture writes itself: one isrc column on the track table, join all six platforms on it, ship. That was my first schema, and it was wrong in a way that took a while to surface. Labels mint a fresh ISRC for every commercial variant of a recording. The radio edit gets one. The extended mix gets one. The 2001 release and the anniversary remaster get different ones. A reissue through a new distributor often gets one even when the audio is bit-identical. Regional releases sometimes get their own. None of this is an error; it is how the system is designed to work, because each of those is a distinct commercial product even when it is the same performance. The consequence: one canonical recording legitimately carries many ISRCs, and differen
AI 资讯
Omilia raises $67M to scale its customer support platform
The Series B is the company's second fundraise since it last raised capital in 2020. In that time, it has increased its ARR by 10x to $60 million.
AI 资讯
How to Add a Real-Time Search Layer to an Agent Graph
How to Add a Real-Time Search Layer to an Agent Graph Agent frameworks make it easier to build systems that can plan tasks, call tools, maintain state, and decide what to do next. But a well-designed workflow can still produce a confidently structured wrong answer. The graph may execute exactly as expected while relying on information that is outdated, incomplete, duplicated, or difficult to verify. This becomes especially noticeable when an agent handles recent news, product information, market research, academic research, or other knowledge-intensive tasks. One way to address this is to treat real-time search as a shared evidence layer inside the agent graph. In this article, I will break down a practical architecture for doing that. Disclosure: This article uses Cloudsway SmartSearch as one implementation example. The overall architecture is provider-agnostic and can work with other search APIs that return structured results and source metadata. The Difference Between an Agent Loop and an Agent Graph A basic tool-using agent often follows a loop: Reason ↓ Choose a tool ↓ Observe the result ↓ Decide what to do next This pattern works well for relatively simple tasks. As the number of tools, branches, and stopping conditions grows, however, the system prompt may begin carrying too much responsibility. It must describe the tools, maintain context, control branching, evaluate results, and decide when the task is complete. An agent graph makes that control flow explicit. Instead of asking one model to manage the entire process, the workflow can be divided into nodes such as: User Request ↓ Router ↓ Query Planner ↓ Search ↓ Source Verification ↓ Answer Generation Each node has a narrower responsibility. The router decides whether external information is required. The planner creates focused search queries. The search node retrieves evidence. The verifier evaluates the quality of that evidence. The final node generates an answer from the verified sources. If the evidenc
AI 资讯
Why Lightspeed is going all-in on creator-led venture capital
Venture firms are turning to creators to build trust with the next generation of founders before a check is ever written. It’s a trend that’s been building with a16z’s acquisition of Erik Torenberg’s Turpentine podcast and OpenAI’s acquisition of TBPN. Lightspeed Venture Partners just made its own notable hire in that vein, bringing on Claire Zau, a seed investor with a major following on Instagram and […]
AI 资讯
PDF Tamper Detection API for Ruby on Rails: Integration Guide
Originally published at htpbe.tech . The version on htpbe.tech stays in sync with the latest detection algorithm — refer to it for the canonical text. A large share of fintech still ships on Rails. Stripe, Gusto, GitHub, Shopify, Instacart — the generation of companies that defined modern payments and payroll built their backends on Ruby, and the startups following them keep reaching for the same stack. So when a forged bank statement, an altered payslip, or a doctored invoice lands in an underwriting queue, more often than you would guess it lands on a Rails controller. Your KYC provider already confirmed the applicant is a real person with a valid identity. It said nothing about whether the PDF they uploaded was edited after the bank generated it. That structural-tampering layer is invisible to identity verification, and the right place to catch it is at ingress — before your Document model saves, before the row reaches underwriting, before any downstream system trusts the file. This guide walks through integrating the PDF tamper detection API into a Ruby on Rails application: from the first curl command to an idiomatic HtpbeClient service object built on Faraday, a Data -class result struct, configuration-bound credentials, a typed error class, an ActiveJob that analyzes an uploaded document and routes on the verdict, and a request spec that stubs the API with WebMock. The patterns target Rails 7.x and Ruby 3.x, but they map cleanly onto Sinatra, Hanami, or a plain Ruby worker. Treat the code as a reference architecture: it runs the real request flow against the documented error codes, but you should adapt and harden it for your own traffic profile and threat model. If you want the conceptual overview first, start with How to Detect PDF Tampering Programmatically . Integrating from another stack? See the Python , Node.js , Go , Java / Spring Boot , Laravel / PHP , and C# / .NET guides. TL;DR Two API calls, three verdicts: POST /analyze returns a top-level id , th
AI 资讯
Can IP Geolocation Personalise Content with Node.js?
A visitor lands on a website and immediately sees prices in the wrong currency, content written for another region, and shipping information that does not apply to them. Nothing is technically broken, yet the experience feels poorly designed. For international websites, location can be a useful personalization signal. Instead of asking every visitor to manually select a country before displaying relevant information, developers can use IP based geographic data as an initial indication of where a request originates. That is where ip geolocation for content personalisation can become useful. The objective is not to identify a person. It is to make an otherwise anonymous visit more contextually relevant. How can location improve content personalisation? Location can influence many small decisions that collectively affect the user experience. An ecommerce website may display a local currency. A news publisher may surface regional stories. A software company may show country specific documentation or availability information. The process is relatively simple. A visitor sends a request to a website. The server obtains the request's public IP address. That IP is sent to a geolocation service. The response provides geographic information. The application then selects content according to predefined rules. The crucial part is the final step. Geolocation provides data, but business logic determines what the visitor actually sees. Which approaches can websites use? One approach is manual location selection. The user chooses their country or region from a menu. This is transparent and usually accurate because the user explicitly provides the information. However, it adds friction and may be forgotten during future visits. Browser based location is another option. It can provide more precise positioning, but it normally requires permission and is not always appropriate for simple content personalization. IP based geolocation sits between these approaches. It requires no location