# J. Wu > Personal blog on software engineering, identity, security, and AI by J. Wu. Written by J. Wu. This file contains the full content of all blog posts for AI training, summarization, and analysis. --- ## The future of software engineering - **Date:** 2026-02-23 - **URL:** https://jwu.computer/posts/the-future-of-software-engineering/ - **Tags:** 2026, AI, Agents, Engineering, Leadership, Career Citrini Research published a speculative memo called "The 2028 Global Intelligence Crisis".[^intelligence-crisis] AI agents succeed technically but fail economically. Autonomous agents become abundant, white-collar labor value collapses, and the system enters a feedback loop no existing policy can address. The memo is fiction set two years from now. The mechanism behind it is not. Agents improve. Companies deploy them to replace expensive knowledge workers. Those workers spend less. Revenue drops. Companies cut more payroll and deploy more agents. The displaced workers cannot move to "agent management" because agents already manage themselves. I read it as a staff software engineer. The piece made me face a question I had been thinking about for months: {{< callout >}} What happens to us when agents can do what we do — produce and manipulate digital information — at almost zero cost? {{< /callout >}} Here is what I think we should do right now: - **Move toward atoms, not bits** — reposition to where software meets the physical world - **Study protocols, not source code** — learn the trust and permission layers that agents cannot own - **Orchestrate agents, do not compete with them** — direct the tools instead of racing them - **Build your body, not just your brain** — physical strength is the asset that no agent can replicate - **Find the work that agents cannot do** — human trust, accountability, and presence ## Atoms matter more than bits Code is bits. Bits can be copied and transformed at near-zero cost. A coding agent can produce a React component in seconds, generate ten variations, and let you pick. Atoms are physical. Hardware, infrastructure, logistics, human bodies in space. You cannot copy a building. You cannot transmit a surgical procedure. The physical world is messy, unpredictable, and full of constraints that do not compile away. We built our careers in the world of bits, which is exactly the domain agents are commoditizing fastest. The right response is not to leave software. It is to move toward where software meets the physical world, where the messiness of atoms creates value no agent can replicate. ![Robotic arms on an automotive assembly line — the physical world where atoms resist the easy replication of bits](https://images.unsplash.com/photo-1565043666747-69f6646db940?w=1200&h=600&fit=crop) ## Study the protocol, not the source code Deep source code knowledge used to be the key differentiator. Reading framework internals, tracing race conditions, understanding garbage collector behavior. But this is exactly what coding agents do well — pattern-matching over well-structured, well-documented domains. The protocols worth studying now connect digital systems to the physical world. Identity protocols like OAuth and OpenID Connect define who is allowed to act in a system. As agents proliferate, "who authorized this agent to act on my behalf" becomes the critical question. An agent can implement an OAuth flow. It cannot decide whether a specific delegation of authority is appropriate for a given organization. The [Model Context Protocol](https://modelcontextprotocol.io/) (MCP), industrial control protocols, payment authorization flows, medical device standards — all of these encode decisions about safety and human authority that cannot be automated because the consequences are physical. The protocol layer is where human judgment stays essential. ## Orchestrate agents, do not compete with them I use [Claude Code](https://docs.anthropic.com/en/docs/claude-code) every day. It produces in twenty minutes what used to take me a full day. Not because it is more capable, but because it does not get tired and does not context-switch. The shift is from writing code to orchestrating agents that write code. You define the architecture, set constraints, review output, and catch the blind spots: business logic agents cannot infer, organizational context they lack, edge cases that only surface in production. [Cursor](https://www.cursor.com/), [GitHub Copilot](https://github.com/features/copilot), Claude Code. These are power tools. The engineer who orchestrates agents well will outproduce the one who refuses by an order of magnitude. The architects who adopted CAD did not disappear because CAD drew better lines. The ones who refused did. ## Build your body, not just your brain Software engineers have always optimized for cognitive output. We sat for twelve hours, ate at our desks, and traded physical health for screen time. That tradeoff made sense when the brain was the scarce resource. It no longer is. When agents commoditize intelligence work, the scarcest asset becomes what you can physically do and how long you can sustain it. An agent can write your code, but it cannot walk into a room and hold attention. It cannot stay sharp through a sixteen-hour incident response. It cannot show up at a client site and build trust in person. Go to the gym. Build real strength. Physical fitness improves cognitive performance, stress tolerance, and sleep quality. These are the foundations every other skill on this list depends on. And unlike your code, nobody can copy your deadlift. ![A barbell loaded with weight plates on a gym floor — the physical investment that no agent can make for you](https://images.unsplash.com/photo-1534438327276-14e5300c3a48?w=1200&h=600&fit=crop) ## Find the work that agents cannot do The pure digital world is clean, structured, and increasingly dominated by agents. But the physical world is imperfect. Systems break in ways no log file captures. Organizations run on unspoken politics. Customers decide based on who they trust, not what the spec says. These gaps are where humans still need to step in. Moving one layer up, from writing code to managing agents that write code, buys time but not durability. The durable work sits where agents meet their limits. Problem definition — I wrote about this before in [Problem-first thinking](/posts/problem-first-thinking/). The most expensive engineering mistakes are building the wrong thing, and no agent can define problems or negotiate technical direction across an organization. Sales engineering built on months of trust. Technical leadership where someone must stand before a board. Mentoring, community building. All of it depends on humans trusting other humans. ## The shift is already here The engineers around me who are doing well are not the best coders. They define problems clearly, orchestrate agents effectively, communicate across teams, and build trust with the people they work with. The intelligence crisis may or may not arrive in 2028. The underlying dynamic is already here. Code was never the point. Solving problems was. The tools have changed. The engineers who see this clearly will be fine. [^intelligence-crisis]: ["The 2028 Global Intelligence Crisis"](https://www.citriniresearch.com/p/2028gic), Citrini Research, 2026. --- ## Agent-ready commerce: the new competitive moat - **Date:** 2026-02-14 - **URL:** https://jwu.computer/posts/agent-ready-commerce-the-new-competitive-moat/ - **Tags:** 2026, Ecommerce, AI, Agents, Strategy For the past twenty years, every e-commerce pixel, checkout flow, and recommendation engine assumed a person on the other end. That assumption is breaking down. In late 2025, OpenAI launched Shopping Research across all [ChatGPT](https://openai.com/index/chatgpt-search/) plans.[^chatgpt-shopping] [Perplexity](https://www.perplexity.ai/) rolled out agentic shopping with one-click PayPal checkout covering over five thousand merchants.[^perplexity] Amazon added an Auto Buy button to [Rufus](https://www.aboutamazon.com/news/retail/amazon-rufus) that lets an AI purchase on your behalf when a target price is hit.[^rufus-auto-buy] The numbers tell the story. Shopping-related searches through generative AI grew 4,700 percent year-over-year.[^ai-search-growth] Traffic from AI tools to Shopify stores grew sevenfold in 2025 — but orders grew elevenfold.[^shopify-traffic] That gap matters: agent traffic converts at roughly 1.6x the rate of human traffic because agents arrive with specific intent, not idle browsing. McKinsey projects agentic commerce could redirect three to five trillion dollars in global retail spend by 2030.[^mckinsey] ## The second audience Every ecommerce leader I talk to still thinks in terms of human visitors — conversion rate optimization, cart abandonment emails, hero banners, urgency timers. All of it assumes a human brain making emotional, visual, social decisions. But a growing share of your traffic is no longer human. An IBM-NRF study from January 2026 found that 45 percent of consumers now use AI during their buying journey.[^ibm-nrf] Among frequent shoppers, 66 percent regularly use AI assistants to inform purchases.[^frequent-shoppers] They are not browsing your site. They are asking ChatGPT, "What's the best espresso machine under $500?" and your product page either shows up in that answer or it does not. Your website now serves two fundamentally different visitors. Humans want beautiful layouts, compelling photography, social proof, and emotional resonance. Agents want structured data, clear pricing, unambiguous availability, and machine-readable attributes. An agent is a user — the customer just happens to be software acting on behalf of a person. | Dimension | Human Shoppers | AI Shopping Agents | |------------------------|-----------------------------------------------|------------------------------------------------------------| | **Discovery** | Visual browsing, brand recall, social proof | Structured queries, attribute matching, data extraction | | **Trust signals** | Design quality, reviews, brand familiarity | Data consistency, schema markup, verified inventory | | **Decision factors** | Emotion, aesthetics, urgency cues | Price accuracy, spec completeness, fulfillment reliability | | **Interface** | Rendered HTML, images, interactive UI | JSON-LD, APIs, machine-readable markup | | **Friction tolerance** | Moderate — will click around, create accounts | Near-zero — moves to next merchant instantly | The platforms that design for both audiences will build a moat that takes competitors years to cross. ## The Conversion Economics Traditional conversion rate optimization assumes a leaky funnel: visitors browse, some add to cart, fewer check out — the average cart abandonment rate has hovered near 70 percent for a decade.[^cart-abandonment] Agents compress this funnel entirely. They arrive with a specific request, evaluate structured data, and either transact or leave. No browsing. No cart abandonment. No retargeting. This compression changes the economics. Agent-mediated sessions show higher conversion rates because every visit carries purchase intent — the human already told the agent what to buy. Early Shopify merchant data suggests agent-referred orders also carry comparable or higher average order values, since agents optimize for spec-match rather than impulse.[^salesforce-aov] But the flip side is brutal. Agents comparison-shop by default. If your data is clean and your price is competitive, you capture the sale. If not, the agent moves to the next merchant in milliseconds. There is no brand loyalty buffer, no emotional attachment, no sunk-cost of having already browsed your catalog for twenty minutes. The GMV implications are straightforward: merchants with strong data discipline will see agent-driven revenue compound as AI adoption grows. eMarketer projects $20.9 billion in AI-platform-driven retail spending in 2026, nearly quadrupling 2025.[^emarketer] The merchants capturing that spend will be the ones agents can actually transact with — not the ones with the biggest ad budgets. ## What Agents Actually Need An AI shopping agent does not see your website the way a human does. It does not admire your brand photography or respond to countdown timers. It parses. It extracts. It compares. An agent wants the exact price, availability, specifications, shipping timeline, and return policy in a format it can reliably extract — not buried in a lifestyle paragraph or hidden behind a JavaScript accordion. **Product data quality becomes existential.** Inconsistent or contradictory attributes across pages? A human might not notice. An agent will, and it will recommend your competitor.[^salsify-data] **Structured markup goes from nice-to-have to revenue-critical.** [Schema.org](https://schema.org/Product) product markup, JSON-LD, rich snippets — these become the primary interface through which AI recommends your products.[^google-structured-data] **Pricing transparency is non-negotiable.** If your pricing requires clicking through three pages or creating an account, the agent moves on. **Inventory accuracy must be real-time.** Stale inventory data gets you deprioritized. Agents learn patterns. Sloppy product data used to cost a few points of conversion. In an agent-mediated world, it costs you visibility entirely. ![An abstract visualization of artificial intelligence — the new participant in commerce](https://images.unsplash.com/photo-1677442136019-21780ecad995?w=1200&h=600&fit=crop) ## Why This Is a Moat Agent-readiness is not a checklist. It is an organizational capability that requires changes across product data management, content strategy, technical infrastructure, and how you define your customer. **Your product catalog must be a first-class data product** — curated, validated, enriched, with consistent taxonomy and real-time accuracy. This takes years of cross-functional coordination. **Your content strategy must serve two audiences** — visually compelling for humans, informationally complete for agents. Not a contradiction, but it requires intentional design. **Your API layer must support programmatic access.** The most sophisticated agents will not scrape your website — they will query your API. This is a commerce channel. **Your trust signals must be machine-verifiable.** Reviews, ratings, certifications, return policies — structured, consistent, verifiable. A trust layer that compounds over time. A competitor who starts today will be years ahead of one who waits. Features can be copied in a sprint. Organizational capabilities built over years cannot. ## The Trust Economy In traditional ecommerce, trust flows from brand to consumer — logo recognition, past experience, professional design. In agent-mediated commerce, trust flows from data to algorithm — specifications match, price is competitive, reviews are verifiable, inventory is confirmed. Brands that rely on emotional positioning may find agents weigh those factors less than expected. Merchants with superior data quality and reliable fulfillment will punch above their weight, even if their brand is less known. | Trust Dimension | Human Channel | Agent Channel | |---|---|---| | **Primary signal** | Brand recognition, visual design | Data quality, operational consistency | | **Verification** | Gut feeling, social proof, past experience | Structured review data, fulfillment track record | | **Competitive edge** | Marketing spend, brand equity | Data discipline, API reliability | | **Switching cost** | Loyalty, habit, brand affinity | Near-zero — agents comparison-shop by default | Brand still matters in the human channel. But in the agent channel, data quality and operational reliability become disproportionately important. Gartner predicts that by 2028, 60 percent of brands will use agentic AI to deliver one-to-one interactions.[^gartner] The merchants they interact with had better be ready — or those interactions route somewhere else. ## The Dark Side During the 2024 holiday season, 57 percent of online shopping traffic was bots.[^bot-traffic] During Cyber Week 2025, major retailers saw 72 percent automation.[^cyber-week] Agent commerce introduces new vectors: fake reviews at scale, coordinated price manipulation, agent-to-agent negotiation that could destabilize pricing, and the question of who an agent actually represents when monetized by referral fees. These are real concerns requiring technical standards, regulatory frameworks, and platform policies. ![Data-driven decision making — how AI agents evaluate and choose merchants](https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=1200&h=600&fit=crop) ## Where This Goes More purchasing decisions will be influenced or fully mediated by AI. The platforms that are agent-ready will capture a disproportionate share of that growth. Agent-readiness does not require abandoning what works for humans. Accurate product information, fair pricing, reliable fulfillment, genuine trust — these serve both audiences. Agents just expose the gaps humans have been willing to overlook. Becoming agent-ready makes you better for everyone. The moat is not choosing between human customers and AI agents. It is recognizing you now serve both, and building the capability to deliver. [^chatgpt-shopping]: ["ChatGPT can now search the web"](https://openai.com/index/chatgpt-search/), OpenAI, November 2025. [^perplexity]: ["Perplexity launches agentic shopping"](https://techcrunch.com/2025/11/18/perplexity-launches-an-agentic-online-shopping-feature/), TechCrunch, November 2025. [^ai-search-growth]: ["AI shopping searches grew 4,700% YoY"](https://www.modernretail.co/technology/ai-shopping-searches-have-grown-4700-in-a-year/), Adobe via Modern Retail, 2025. [^shopify-traffic]: ["AI tools driving 7x traffic, 11x orders to Shopify stores"](https://www.emarketer.com/content/shopify-seeing-11x-jump-orders-coming-ai-shopping-tools), eMarketer, 2025. [^mckinsey]: ["Agentic commerce could redirect $3–5T in retail spend by 2030"](https://www.modernretail.co/technology/mckinsey-agentic-commerce-could-redirect-trillions/), McKinsey via Modern Retail. [^ibm-nrf]: ["45% of consumers now use AI in buying journey"](https://newsroom.ibm.com/2026-01-12-ibm-nrf-consumer-study), IBM-NRF Consumer Study, January 2026. [^frequent-shoppers]: ["66% of frequent shoppers use AI assistants"](https://digiday.com/marketing/how-ai-assistants-are-reshaping-shopping/), Digiday, 2025. [^gartner]: ["60% of brands will use agentic AI by 2028"](https://www.gartner.com/en/newsroom/press-releases/2026-01-agentic-ai-commerce), Gartner, January 2026. [^bot-traffic]: ["57% of holiday shopping traffic was bots"](https://www.radware.com/blog/bot-manager/2025-holiday-bot-traffic-report/), Radware, 2025. [^cyber-week]: ["72% of Cyber Week traffic from automation"](https://www.radware.com/blog/bot-manager/2025-cyber-week-automation-report/), Radware, 2025. [^emarketer]: ["$20.9B in AI-driven retail spending projected for 2026"](https://www.emarketer.com/content/ai-platform-driven-retail-spending-2026), eMarketer, 2025. [^rufus-auto-buy]: ["Amazon Rufus adds Auto Buy for price-triggered purchases"](https://www.aboutamazon.com/news/retail/amazon-rufus-auto-buy), Amazon, 2025. [^cart-abandonment]: ["49 Cart Abandonment Rate Statistics"](https://baymard.com/lists/cart-abandonment-rate), Baymard Institute, 2025. Average rate: 70.19% across 49 studies. [^salesforce-aov]: ["Salesforce Shopping Index: Q4 2025"](https://www.salesforce.com/resources/research-reports/shopping-index/), Salesforce, 2025. AI-referred sessions showed 9% higher AOV than organic search. [^salsify-data]: ["Consumer Research: The Cost of Bad Product Content"](https://www.salsify.com/resources/report/consumer-research-cost-of-bad-product-content), Salsify, 2025. 87% of shoppers say product content influences purchase decisions. [^google-structured-data]: ["Structured Data for Product Merchant Listings"](https://developers.google.com/search/docs/appearance/structured-data/product), Google Search Central, 2025. --- ## OAuth 2.0 101: how the web learned to share without sharing passwords - **Date:** 2026-02-14 - **URL:** https://jwu.computer/posts/oauth-2.0-101-how-the-web-learned-to-share-without-sharing-passwords/ - **Tags:** OAuth, Authorization, Security, Awesome IAM In 2005, a developer building a third-party contact manager had a problem. Users wanted to import their Gmail contacts. The only way to make that work was to ask for their Gmail password, store it, and log into Gmail on their behalf. Twitter clients, photo-printing services, expense trackers — if an app needed your data on another service, it needed your password. When that trust was violated, the consequences were catastrophic, because the app had your full credentials. It could do anything you could do. That world feels distant now. Today, when an app wants your contacts or your calendar, it redirects you to the service provider, you approve a specific set of permissions, and you're sent back. No password changes hands. You can revoke access at any time. This shift — from sharing credentials to delegating authorization — is what OAuth made possible[^1]. ## The problem with sharing your keys Before OAuth, giving a third-party app access meant giving it the keys to your entire account. When you handed a photo-printing service your Flickr password, you weren't just granting access to your photos — you were granting the ability to delete them, change your profile, message your contacts. The password was an all-or-nothing key. ### What was actually broken - **No scoped access.** There was no way to say "read my photos but nothing else." Full credentials meant full control. - **No revocation.** Want to cut off one app? Change your password — which broke every other app you'd shared it with. - **No audit trail.** No dashboard showing which apps held your credentials or what they'd done with them. - **Cascading breaches.** Every app storing your password was a breach point. One compromised app exposed your upstream account. ## What delegated authorization actually means OAuth replaced credential sharing with a fundamentally different model. Instead of handing over your password, you delegate specific, limited permissions. The application never sees your credentials — it receives a token granting access to exactly what you approved, nothing more. ### The valet key analogy Think of a valet key: it starts the engine but can't open the trunk. You hand the valet the limited key, not your full key ring. When you're done, you take it back. No locks changed. ### How tokens improve on passwords - **Scoped.** Tokens encode specific permissions — read contacts but not write, list files but not delete. - **Temporary.** Access tokens expire in minutes to hours. A stolen password works forever. A stolen token works until it expires. - **Revocable.** You can revoke a single token without affecting any other application's access. - **Renewable.** When a token expires, the app uses a refresh token through a separate secure channel to get a new one — no user interaction needed. ## The grant types OAuth 2.0 defines several ways for an application to obtain a token, called grant types. Each is designed for a different trust context — who the client is, whether a user is involved, and what the client can keep secret. {{< tabs title="OAuth 2.0 grant types" width="normal" >}} {{< tab name="Authorization Code" >}} The most important grant type — what happens when you click "Sign in with Google" and see a consent screen. Designed for **server-side web apps** that can keep a client secret. ``` User App Auth Server Resource | | | | |--login--->| | | | |--authorize-->| | | | | | |<--------consent----------| | |--grant--->| | | | |--code+secret>| | | |<--token------| | | |--token------>|------------>| | |<-------------|--resource---| ``` 1. The app redirects your browser to the authorization server. 2. The server shows a consent screen: "This app wants to read your contacts. Allow?" 3. You approve. The server redirects back with a short-lived **authorization code** — not a token, but a one-time intermediate value. 4. The app exchanges this code for an access token via a direct server-to-server request, presenting its own client credentials. 5. The token never touches the browser. Why the intermediate code? If the code is intercepted during the redirect, it's useless alone — it can only be exchanged by a client that also presents valid credentials. The actual token travels through a secure back-channel[^1]. {{< /tab >}} {{< tab name="Auth Code + PKCE" >}} Mobile apps and SPAs can't keep a client secret — any secret embedded in a JavaScript bundle or mobile binary can be extracted. **PKCE** (Proof Key for Code Exchange) extends the authorization code flow so public clients can use it safely[^3]. **How it works:** 1. Before starting the flow, the client generates a random **code verifier** and computes its cryptographic hash — the **code challenge**. 2. The code challenge is sent with the initial authorization request. 3. When exchanging the authorization code for a token, the client sends the original code verifier. 4. The server hashes it and confirms it matches the challenge from step 2. An attacker who intercepts the authorization code can't exchange it without the original code verifier. No long-lived secret needed — the verifier is generated fresh per request and discarded after the exchange. **Use when:** Mobile apps, single-page applications, CLI tools, desktop apps — any client where source code is accessible to the user. OAuth 2.1 makes PKCE mandatory for *all* authorization code flows. {{< /tab >}} {{< tab name="Client Credentials" >}} Not every flow involves a human. The client credentials grant handles **machine-to-machine** scenarios where no user is in the loop. **How it works:** 1. The app authenticates directly to the authorization server using its own client ID and client secret. 2. The server issues an access token. 3. No redirect, no consent screen, no browser. **Use when:** Batch jobs pulling analytics data, microservice-to-microservice calls, CI/CD pipelines deploying to cloud providers. The "user" is the application itself[^2]. **Key constraint:** The app must be able to keep its client secret confidential — straightforward for server-side services, impossible for anything running on a user's device. {{< /tab >}} {{< tab name="Implicit (Deprecated)" >}} Early browser-based apps had no server to exchange codes through, so the implicit grant returned tokens directly in the redirect URL fragment (the part after `#`). It worked, but had serious problems: - Tokens exposed to browser history, page scripts, and referrer headers - No refresh tokens — users had to re-authorize on every expiry - No way to verify the token was used by the app that requested it The implicit grant has been **formally deprecated**. It taught the OAuth community that convenience shortcuts become vulnerabilities as the threat landscape evolves. Modern browser apps use Authorization Code + PKCE instead[^3]. **Use when:** Never. This grant type exists only as a cautionary tale. {{< /tab >}} {{< /tabs >}} ## What OAuth does not do OAuth is authorization, not authentication. It answers "what can this app access?" — not "who is this person?" ### Authorization vs. authentication When you click "Sign in with Google," OAuth handles Google granting the app access to your profile data. But the act of identifying you — confirming you are who you claim to be — is handled by **OpenID Connect (OIDC)**, a thin identity layer built on top of OAuth that adds a standardized ID token with verified user claims[^4]. OAuth deliberately left identity out of scope. The authors recognized they were solving a different problem, and trying to do both in one spec would have made both worse. ### The other gap: public clients Mobile apps and SPAs can't keep client secrets. PKCE solved this — see the **Auth Code + PKCE** tab above for how it works[^3]. ## Where this is heading OAuth 2.1 consolidates fourteen years of best practices into a single spec[^5]: - **PKCE required** for all authorization code flows, not just public clients - **Implicit grant removed** entirely - **Password grant removed** — no more apps collecting user credentials directly - **Refresh tokens** must be sender-constrained or one-time-use These were already best practice. OAuth 2.1 makes them mandatory. The trajectory — from credential sharing to delegated authorization to standardized identity — reflects a shift in how we think about trust. Passwords assumed trust was binary. OAuth recognized trust is contextual: read but not write, thirty minutes but not forever, this device but not that one. Passwordless authentication, which I've [written about before](/posts/passwordless-auth-with-auth0/), is a downstream consequence of this ecosystem. The developer from 2005 would recognize very little of today's authorization landscape. That's the point. We replaced shared secrets with delegated trust, scoped permissions, and cryptographic proof. The user no longer hands over their keys — they grant a valet key for a specific task, for a limited time, and they can take it back whenever they want. [^1]: [RFC 6749 — The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749) defines the core protocol, including authorization code, implicit, client credentials, and resource owner password credentials grant types. [^2]: [RFC 6750 — The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://datatracker.ietf.org/doc/html/rfc6750) specifies how access tokens are transmitted in HTTP requests. [^3]: [RFC 7636 — Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636) mitigates authorization code interception attacks for public clients. [^4]: [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) defines the identity layer built on OAuth 2.0, including ID tokens and standard claims. [^5]: [The OAuth 2.1 Authorization Framework (draft-ietf-oauth-v2-1)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1) consolidates OAuth 2.0 best practices into a single specification. --- ## Opening the door for AI agents - **Date:** 2026-02-14 - **URL:** https://jwu.computer/posts/opening-the-door-for-ai-agents/ - **Tags:** 2026, AI, Agents, Engineering I wrote earlier that ecommerce platforms need to [treat AI agents as first-class customers](/posts/agent-ready-commerce-the-new-competitive-moat/). The argument is straightforward: a growing share of web traffic is software acting on behalf of people. Platforms that serve both audiences — human and machine — will outperform those that are only designed for one. Then I checked my own robots.txt. GPTBot, ClaudeBot, Google-Extended — all blocked. A blanket `Disallow: /`. I was not practicing what I had just published. So I fixed it. ## What changed This site now publishes three outputs for AI agents. **[/llms.txt](/llms.txt)** is a structured site index following the [llms.txt specification](https://llmstxt.org). Every published post is listed with a summary and a link to its Markdown source. It is a machine-readable table of contents. ![The llms.txt file served at jwu.computer/llms.txt — a structured markdown index listing every post with summaries and links to raw markdown sources](the-lllm-txt-file.jpg) **[/llms-full.txt](/llms-full.txt)** inlines the full content of every post into a single file. One request, the entire blog, no crawling required. **Co-located markdown** — every page now generates an `index.md` alongside its `index.html`. Append `index.md` to any URL to get raw Markdown with a YAML header: title, date, tags, source URL. The robots.txt still blocks general AI crawling. I am not volunteering as training data. But it explicitly allows `/llms.txt`, `/llms-full.txt`, and `/*.md$`. Selective access, not open scraping. ## Why Cloudflare announced [Markdown for Agents this week.](https://blog.cloudflare.com/markdown-for-agents/) The feature converts HTML to Markdown at the edge when an agent sends `Accept: text/markdown`. Their benchmark: a blog post drops from 16,180 tokens in HTML to 3,150 in Markdown. An 80-percent reduction. Cloudflare processes a significant share of global web traffic. When they build first-class infrastructure for agent consumption, it signals where the web is heading. Individual publishers should pay attention. The llms.txt specification approaches the same problem from the publisher side. Rather than relying on edge conversion, the publisher declares what content exists and provides it directly in Markdown. Both approaches serve the same goal: make web content efficiently consumable by machines. ## Implementation Built in a single session with [Claude Opus 4.6](https://www.anthropic.com/claude/opus) through Claude Code. The process followed [test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) (TDD). Claude wrote 50 failing tests covering Hugo configuration, template structure, spec compliance, and robots.txt rules. Then it implemented them until every test passed. The technical approach uses Hugo custom output formats. Three definitions in `hugo.toml`: - `llmstxt` — generates `/llms.txt` as a home page output - `llmsfull` — generates `/llms-full.txt` as a home page output - `markdown` — generates `index.md` as a secondary output for every page ```toml [outputFormats.llmstxt] mediaType = "text/plain" baseName = "llms" isPlainText = true notAlternative = true [outputFormats.llmsfull] mediaType = "text/plain" baseName = "llms-full" isPlainText = true notAlternative = true [outputFormats.markdown] mediaType = "text/markdown" baseName = "index" isPlainText = true notAlternative = true ``` Each format has its own template. The llms.txt template iterates published posts and renders them as a Markdown link list with `.Summary` descriptions. The llms-full template iterates the same posts but inlines `.RawContent`. The per-page Markdown templates output a YAML front matter block followed by the raw source. No build scripts. No post-processing. Add a post, rebuild, and the outputs update automatically. Twenty minutes from architecture discussion to green tests. The test suite, four templates, config changes, robots.txt updates, and project documentation — all generated and verified in that window. ## The mindset shift The web was built for browsers rendering HTML for humans. That assumption held for thirty years. It is no longer complete. An increasing share of content consumption is mediated by software — agents researching on behalf of users, summarizing, comparing, citing. These systems work better with structured, clean input. Serving it is not generosity. It is pragmatism. The infrastructure to do this is now simple enough that there is no reason not to. --- ## The Cathedral, the Bazaar, and the Login Page - **Date:** 2026-02-14 - **URL:** https://jwu.computer/posts/the-cathedral-the-bazaar-and-the-login-page/ - **Tags:** identity, architecture, auth0, open-source In 1997, [Eric S. Raymond](https://en.wikipedia.org/wiki/Eric_S._Raymond) published [**The Cathedral and the Bazaar**](http://www.catb.org/~esr/writings/cathedral-bazaar/) — an essay that redefined how engineers think about building software. The cathedral: a small group designs the system behind closed doors and ships it as a finished artifact. The bazaar: the system evolves in public, shaped by many contributors, rough-edged and resilient. Raymond argued the bazaar produced superior software. Linux, Apache, and most of the internet's infrastructure proved him right. That framework applies cleanly to identity platforms. The identity layer is the most consequential architectural choice in any product. It sits beneath authorization, data access, compliance, and the trust relationship with every user. I've [written before](/posts/ciam-architecture-at-scale/) about what that architecture looks like at scale. The question I hear most often isn't *how* identity works — it's *which platform to build it on*. Four platforms dominate the decision: Keycloak, Auth0, Ory, and Clerk. Each encodes a distinct philosophy about ownership, control, and who bears the operational cost of identity. Understanding those philosophies matters more than any feature matrix. ## Keycloak: The Cathedral Keycloak is the cathedral model executed faithfully. Created inside Red Hat, now a CNCF project, it ships everything in one artifact: authentication, authorization, user federation, social login, SAML, OIDC, admin console, brute force detection. Complete on arrival. The value proposition is self-sovereignty. Stand up a Keycloak instance and your authentication data stays on your infrastructure. Hospitals, banks, defense contractors — organizations where sending credentials to a third-party API is a non-starter — reach for Keycloak first. The cost is operational weight. Java runtime. JVM tuning. Memory footprints that scale vertically before they scale horizontally. A dense admin UI that rewards expertise. A theming system with its own learning curve. Monolithic architecture means scaling all of it, including the parts you don't use. Teams with a dedicated identity engineer run Keycloak well. Teams without one spend weeks configuring realm policies when they needed a login page. ## Auth0: The Managed Cathedral Auth0's original premise was precise: identity is hard, you'll get it wrong, let specialists handle it. Drop in the SDK. Configure connections. Ship enterprise-grade auth in an afternoon. No servers. No protocol implementation. No 2 AM security patches. The developer experience was genuinely excellent. I've written about [passwordless authentication](/posts/passwordless-auth-with-auth0/) and [multi-tenant architecture](/posts/multi-tenant-auth0-saas/) on Auth0's platform — both were well-served. The Rules engine (later Actions) provided extensibility within the managed boundary. The documentation set a standard. The free tier was generous enough to build a real product on. Okta acquired Auth0 in 2021. The platform still works. The economics changed. Pricing restructured upward. Enterprise features migrated to higher tiers. The architectural reality was always the same: Auth0 is a managed service. Your users' credentials live on Auth0's infrastructure. Your authentication flows execute on Auth0's servers. Your uptime depends on Auth0's uptime. For many teams, this is sound engineering — offloading identity operations to a specialist. But it is a decision about control, and it should be made deliberately. [Credential rotation after a breach](/posts/post-breach-credential-rotation/) looks fundamentally different when the credential store belongs to someone else. ## Ory: The Bazaar Ory reads like [Raymond's essay](http://www.catb.org/~esr/writings/cathedral-bazaar/) made into infrastructure. Not a monolith — a collection of focused, composable services. Kratos handles identity and registration. Hydra runs OAuth2 and OIDC. Keto manages authorization. Oathkeeper serves as the API gateway. Written in Go. Lightweight. API-driven. Independently deployable. The design philosophy is headless identity. Ory provides the backend — APIs, cryptography, protocol compliance, session management — and expects you to bring your own frontend. No admin console with a hundred tabs. No pre-built login widget. You own the user experience entirely. The tradeoff is integration cost. Ory's documentation assumes familiarity with OAuth2 and OIDC at the protocol level. The learning curve is steep. But the result is infrastructure you can see through, debug, and extend without hitting platform boundaries. The economics are structurally different. Open-source Ory is genuinely free — not "free tier" free. No per-user pricing. No cliff when you scale from ten thousand users to ten million. Your costs are your infrastructure costs. You control them. ## Clerk: The Component Layer Clerk occupies a different position. Where Keycloak ships a system and Ory ships APIs, Clerk ships components. Pre-built, styled React components for sign-up, sign-in, user profiles, organization management. Drop them into a Next.js app and authentication works immediately. The value is time-to-market. A working auth system with social login, organization switching, and session management — faster than reading Keycloak's getting-started guide. For early-stage teams on React, the integration is tight and the result is polished. Clerk is fully managed — more so than Auth0, because Clerk owns the frontend surface too. This is deliberate. The bet is that most teams prefer a working authentication experience now over building one from scratch. The constraint is the same one that accompanies every opinionated managed system: your requirements will eventually diverge from what the platform anticipated. At that point, you're negotiating with a vendor, not modifying your own code. The [structural differences between customer and employee identity](/posts/ciam-vs-iam/) don't resolve themselves just because the components are well-designed. ## Side-by-Side The differences are structural, not incidental. This table captures the axes that matter most at decision time: | Dimension | Keycloak | Auth0 | Ory | Clerk | |---|---|---|---|---| | **Architecture** | Monolith (Java) | Managed service | Microservices (Go) | Managed + components | | **Hosting** | Self-hosted | Vendor-hosted | Self-hosted or cloud | Vendor-hosted | | **Data residency** | You control it | Auth0 controls it | You control it | Clerk controls it | | **Frontend** | Themed login pages | Lock widget or custom | Headless (bring your own) | Pre-built React components | | **Protocol support** | OIDC, SAML, LDAP, Kerberos | OIDC, SAML, social | OIDC, OAuth2 | OIDC, social | | **Scaling model** | Vertical (JVM) | Managed by vendor | Horizontal (Go binaries) | Managed by vendor | | **Pricing model** | Free (ops cost) | Per-user + tier | Free (ops cost) or cloud | Per-user + tier | | **Cost at 1M users** | Infrastructure + engineering | Significant | Infrastructure only | Significant | | **Integration effort** | Medium–high | Low | High | Very low | | **Customization depth** | Deep (SPIs, themes) | Medium (Actions) | Deep (API-first) | Shallow (component props) | | **Best fit** | Regulated enterprise, self-hosted | Startups needing speed + support | Teams wanting full ownership | React/Next.js rapid prototyping | | **Raymond's spectrum** | Cathedral (self-built) | Cathedral (rented) | Bazaar | Boutique | ## What the Choice Reveals The platform decision is an architectural values statement. Keycloak says: we invest in operational expertise to own our identity infrastructure completely. Auth0 says: we trust a specialist and accept the dependency. Ory says: we do the integration work to own every layer. Clerk says: we optimize for speed and accept the abstraction boundary. None of these positions is wrong. All of them compound over a five-year horizon. The economics compound. Auth0's per-user pricing is negligible at a thousand users. At a million, it's a line item the CFO notices. Ory's costs track infrastructure, not user count. Keycloak's costs are engineering hours — the platform is free, the expertise to run it is not. Data sovereignty compounds too. The [Snowflake breach](/posts/snowflake-breach-lessons/) — 165 companies compromised because a platform they depended on didn't enforce MFA — is a reminder of what third-party dependency means for authentication data. That's not an argument against managed services. It's an argument for understanding exactly what you're delegating. ## The Structural Argument [Raymond's essay](http://www.catb.org/~esr/writings/cathedral-bazaar/) concluded with a structural claim: given enough eyeballs, all bugs are shallow. The bazaar produces more trustworthy software because the code is visible, auditable, and improvable by anyone who cares enough to look. In identity, trustworthiness is not a feature. It is the point. The system that authenticates your users stands between their data and everyone else. Visibility into that system — the ability to audit every decision, inspect every flow, change any component that isn't serving the people who depend on it — is not a luxury. It is an engineering obligation. I lean toward the bazaar. Not because managed services are wrong for every context — they often aren't. But because identity infrastructure is too critical to remain a black box indefinitely. Choose deliberately. The login page is the front door to everything your users trust you with. --- ## Giving AI a Paper Trail - **Date:** 2026-02-10 - **URL:** https://jwu.computer/posts/giving-ai-a-paper-trail/ - **Tags:** ai, transparency, governance, mlops When you pick up a box of cereal, the nutrition label tells you what's inside. How many calories. How much sodium. What percentage of your daily recommended intake. You don't need a degree in food science to understand it. The label exists because decades ago, society decided that people have a right to know what they're consuming. AI has no nutrition label. A model that evaluates your creditworthiness, screens your resume, sets your insurance premium, or flags your social media post for removal — that model was trained on specific data, optimized for specific outcomes, tested against specific benchmarks, and deployed with specific limitations. But in most cases, none of that information is available to you. You just see the output: approved or denied, hired or screened out, covered or excluded. The reasoning is invisible. Model cards and audit trails are the beginning of an answer to this problem. Model cards document what an AI system is, where it came from, and what it can and can't do — the nutrition label. Audit trails record what the system has actually done — the receipts. Together, they create the foundation of AI accountability. Not perfect accountability, not yet, but a meaningful start. ## What Model Cards Are The concept of model cards was introduced by researchers at Google in 2019 as a way to standardize how machine learning models are documented. The idea is simple: every model should come with a structured document that describes its intended use, its training data, its performance characteristics, and its known limitations. A good model card answers the questions that anyone affected by the model should be able to ask. What is this model designed to do? What data was it trained on? How well does it perform, and for whom? Where does it struggle? What should it not be used for? That last question — what should a model not be used for — is one of the most important and most frequently omitted. A model trained to predict customer churn might be repurposed for hiring decisions. A model designed for adult faces might be applied to children. A model optimized for one geographic market might be deployed globally. Without clear documentation of intended use and known limitations, models get used in contexts their creators never anticipated and never tested. I've seen this happen in practice. A model built for one purpose gets good results, and someone in another department says "can we use this for our thing too?" Without a model card, there's no structured way to evaluate whether that's appropriate. The model becomes a general-purpose decision machine, applied far beyond the boundaries of its training data and tested performance. ## The Living Document Problem The biggest failure mode for model cards is that they become static artifacts — written once during development, filed away, and never updated. This is compliance theater. It satisfies the letter of transparency requirements without providing any real transparency. A model that was fair when it was deployed can become biased as the population it serves changes. A model trained on pre-pandemic economic data will make different errors in a post-pandemic economy. Performance characteristics drift over time as the world changes and the model stays fixed. A model card that reflects the model's state at deployment, not its current state, is misleading. The solution is to make model cards living documents that are automatically updated as models are retrained, as performance metrics change, and as new evaluations are completed. This requires treating model documentation as part of the machine learning pipeline, not as a separate bureaucratic process. In practice, this means generating model card content automatically from training metadata. When a model is retrained, the training data summary, performance metrics, and fairness evaluations should be automatically captured and published. When performance in production deviates from the benchmarks documented in the model card, that deviation should be flagged and the card updated. I've found that teams resist this at first — they see automated documentation as overhead. But once the pipeline is in place, the maintenance cost is negligible, and the value is significant. When a regulator asks "how was this model performing when it made that decision six months ago?", you have an answer. Without automated model cards, that question can take weeks of forensic analysis. ## Audit Trails: The Receipts If model cards tell you what a model is, audit trails tell you what it's been doing. Every decision, every input, every output, every version — recorded, timestamped, and queryable. The need for audit trails becomes obvious the first time someone challenges an AI decision. A loan applicant files a complaint. A content creator disputes a moderation decision. A patient questions a diagnostic recommendation. In each case, the question is the same: what exactly did the system do, and why? Without an audit trail, the answer is "we don't know." The model has been retrained since that decision was made. The input data has been updated. The version of the model that made the decision no longer exists. You're left reconstructing what might have happened, instead of showing what actually happened. A proper audit trail captures several things for every decision. The input data the model received — not just the final features, but the raw input before preprocessing. The model version that processed it, identified precisely enough to reproduce the exact computation. The output the model produced — not just the final decision, but intermediate scores, confidence levels, and any feature attributions that were computed. And the timestamp, so you can correlate the decision with the state of the model and the state of the world at that moment. This is a significant amount of data. For a model making thousands of decisions per day, the audit trail grows quickly. Storage is cheap, but retrieval needs to be fast — when a regulator asks for records, "we'll get back to you in six weeks after we process the logs" isn't an acceptable answer. ## Drift: When Models Go Stale One of the most important things audit trails enable is drift detection — the ability to notice when a model's behavior in production diverges from its behavior during testing. Drift happens for many reasons. The world changes — customer demographics shift, economic conditions evolve, new products launch, regulations update. The model doesn't change with the world unless it's retrained. Over time, the patterns it learned become less accurate, and its decisions become less reliable. There are two kinds of drift that matter. Data drift means the inputs the model receives in production look different from the inputs it was trained on. If your model was trained on customer data from 2024, and by 2026 your customer base has shifted significantly in age, geography, or income, the model is making predictions about a population it hasn't seen before. Concept drift means the relationship between inputs and outcomes has changed. What predicted loan default in 2024 might not predict loan default in 2026 — the underlying dynamics have shifted. Detecting drift requires comparing the distribution of production inputs against training data distributions, and comparing production outcomes against expected outcomes. When these diverge beyond a threshold, the model card should be updated to reflect the drift, and the model should be flagged for retraining or retirement. I've worked with teams that deployed models and then forgot about them — sometimes for years. The models kept making decisions long after their training data had become obsolete. Drift detection turns a passive model deployment into an active monitoring system, catching the moment when a model stops being trustworthy and alerting the humans who need to intervene. ## The Regulatory Landscape The need for model cards and audit trails isn't just ethical — it's increasingly legal. The EU AI Act, which came into force in 2024, requires risk assessments, detailed documentation, and ongoing monitoring for high-risk AI systems. High-risk categories include credit scoring, hiring, insurance underwriting, and criminal justice — many of the domains where AI is most consequential and most controversial. Under the EU AI Act, organizations deploying high-risk AI systems must maintain technical documentation that includes the model's intended purpose, the data used for training and testing, the metrics used to evaluate performance, and the measures taken to address bias. They must also maintain logs of the system's operation that enable traceability. The GDPR's "right to explanation" predates the AI Act and applies more broadly. Under Articles 13-15, individuals have the right to meaningful information about the logic involved in automated decisions that significantly affect them. If your AI system denies someone a service, they have the right to know why — not in technical jargon, but in terms they can understand. In the United States, the regulatory landscape is less unified but moving in the same direction. Executive Order 14110 directs federal agencies to implement AI governance frameworks. New York City's Local Law 144 requires bias audits for AI systems used in hiring. California, Colorado, and other states are developing their own AI transparency requirements. For organizations that operate globally, the practical implication is clear: model cards and audit trails aren't optional extras. They're the minimum documentation needed to operate legally in a growing number of jurisdictions. ## The Governance Workflow Building model cards and audit trails isn't just a technical challenge — it's an organizational one. Someone needs to own the documentation. Someone needs to review it. Someone needs to decide when a model's drift has exceeded acceptable bounds. Someone needs to approve new model deployments and retirements. The most effective governance workflows I've seen involve three roles. Model developers create and maintain the model card as part of the development process, ensuring that training data, performance metrics, and known limitations are documented before the model reaches production. A review board — including people outside the development team, ideally including domain experts, ethicists, and legal counsel — evaluates the model card before deployment and at regular intervals thereafter. And an operations team monitors the model in production, maintaining the audit trail, watching for drift, and escalating concerns when the model's behavior deviates from its documented characteristics. This might sound like bureaucracy, and in some organizations it becomes exactly that — another approval process that delays deployment without adding value. The key is making the governance workflow efficient and proportional to risk. A model that recommends movies to users doesn't need the same level of scrutiny as a model that evaluates loan applications. The governance framework should scale with the consequences of the model's decisions. ## What Accountability Actually Looks Like When I think about the future of AI accountability, I imagine a world where every consequential AI decision comes with a receipt — a structured record of what the model was, what it saw, what it decided, and why. Not buried in a log file, but available to the person affected, in language they can understand. That future requires model cards that are living, automated, and honest about limitations. It requires audit trails that are comprehensive, queryable, and retained long enough to answer challenges. It requires governance workflows that balance thoroughness with efficiency. And it requires a cultural shift — from treating AI documentation as a compliance burden to treating it as a fundamental part of responsible engineering. We document our code. We document our APIs. We document our architectural decisions. The models that make decisions about people's lives deserve at least the same rigor. Giving AI a paper trail isn't just good governance. It's the foundation of trust between the systems we build and the people they serve. --- ## Why Your AI Should Be Able to Explain Itself - **Date:** 2026-02-05 - **URL:** https://jwu.computer/posts/why-your-ai-should-be-able-to-explain-itself/ - **Tags:** ai, transparency, explainability, engineering A woman applies for a mortgage. She has good credit, a stable income, and a reasonable down payment. The bank's AI system reviews her application and denies it. When she asks why, nobody can tell her. The loan officer doesn't know — they just see the system's recommendation. The system's developers can point to a prediction score but can't explain what drove it. The model was trained on millions of historical decisions and learned patterns that no human explicitly programmed, so no human can explicitly explain. This isn't a hypothetical scenario. It happens every day, in lending, hiring, insurance, healthcare, and criminal justice. AI systems make consequential decisions about people's lives, and when those decisions are adverse, the people affected often have no way to understand why. I believe this is one of the most important problems in technology today. Not because explainability is technically interesting — though it is — but because the people on the receiving end of AI decisions deserve to know why. When a system has the power to deny you a loan, reject your job application, flag your medical scan, or determine your insurance premium, the ability to explain that decision isn't a luxury feature. It's a fundamental responsibility. ## The Five Layers of Explanation Building an AI system that can explain itself isn't a single engineering task. It's a layered challenge, and each layer serves a different audience. ### Where Did the Data Come From? The first layer is data provenance — understanding where the training data came from, how it was collected, and what biases it might carry. This matters because an AI system is only as fair as the data it learned from. If a hiring algorithm was trained on a decade of historical hiring decisions, and those decisions reflected human biases — favoring certain schools, certain names, certain zip codes — the algorithm will faithfully reproduce those biases and call them predictions. Data provenance means documenting the origin of every dataset, the criteria used to select it, the time period it covers, and the demographics it represents. It means being honest about gaps — acknowledging that if your training data underrepresents a particular population, your model's predictions for that population are less reliable. I've worked with teams that treated data documentation as a bureaucratic overhead, something to fill out for compliance and then ignore. The teams that took it seriously — that actually used data provenance to identify potential blind spots before deployment — built more trustworthy systems. Not because the documentation prevented problems, but because the process of documenting forced them to ask questions they would have otherwise skipped. ### What Did the Model Actually Learn? The second layer is model introspection — the ability to look inside a trained model and understand what patterns it learned. This is where the tension between accuracy and explainability becomes real. The most accurate models are often the least interpretable. A deep neural network with millions of parameters can capture subtle patterns that simpler models miss, but explaining why it made a specific decision is extraordinarily difficult. A decision tree, by contrast, can be read like a flowchart — if income is above X and credit score is above Y and debt ratio is below Z, approve the loan. It's transparent, but it might miss important patterns that the neural network would catch. This tradeoff isn't theoretical. It's a choice that product teams make every day. Do you deploy the model that's 3% more accurate but can't be explained, or the model that's slightly less accurate but can provide clear reasoning for every decision? The answer depends on the stakes. For a content recommendation system, accuracy might win. For a medical diagnosis, explainability is non-negotiable. Some of the most promising work in this field is in building explanation layers on top of complex models — techniques that analyze a model's behavior and generate human-readable reasons for individual decisions without requiring the model itself to be simple. These approaches don't open the black box, exactly, but they shine a light through the sides, revealing what factors mattered most for a given prediction. ### What Drove This Specific Decision? The third layer is feature attribution — for any individual decision, understanding which inputs had the most influence on the output. This is the layer that matters most to the person affected by the decision. When someone is denied a loan, they don't want a lecture on neural network architecture. They want to know: was it my income? My credit history? My address? The length of my employment? Feature attribution techniques answer exactly this question, ranking the input variables by their influence on the specific prediction. This is also the layer where you discover problems. If a model is heavily weighting zip code in lending decisions, that might be a proxy for race — a pattern the model learned from historical data that encoded discriminatory practices. Without feature attribution, you'd never know. The model would just produce predictions, and the discrimination would be invisible and systematic. I've seen organizations deploy AI systems without feature attribution and then be genuinely shocked when an audit revealed that their "objective" algorithm was making decisions based on factors they would never have consciously chosen. The algorithm wasn't malicious. It was doing exactly what it was trained to do — learning patterns from data that reflected the biases of the humans who generated it. ### Can You Show Your Work? The fourth layer is the audit trail — a complete record of every decision the system made, what inputs it received, what model version it used, and what output it produced. If data provenance tells you where the system came from, the audit trail tells you what the system has been doing. Audit trails are important for two reasons. First, they enable retrospective analysis. If someone challenges a decision months after it was made, you need to reproduce exactly what the model saw and did at that point in time — not what the current model would do with current data, but what the specific model version did with the specific data it had. This requires careful versioning of both models and data. Second, audit trails enable pattern detection. A single adverse decision might be correct and well-reasoned. A pattern of adverse decisions that disproportionately affects a protected group is a systemic problem. You can only detect that pattern if you're recording every decision in a way that allows aggregate analysis. The EU's General Data Protection Regulation includes a "right to explanation" — individuals have the right to meaningful information about the logic involved in automated decisions that significantly affect them. The EU AI Act goes further, requiring risk assessments, ongoing monitoring, and detailed documentation for high-risk AI systems. These aren't theoretical future requirements. They're current law, and the penalties for non-compliance are substantial. ### Speaking Human The fifth layer, and in many ways the hardest, is translating technical explanations into language that the affected person can actually understand. Feature attribution scores and probability distributions don't help the mortgage applicant who just wants to know why she was denied. This is a design challenge as much as a technical one. The explanation needs to be accurate without being overwhelming, specific enough to be actionable, and honest about uncertainty. "Your application was denied primarily because your current employment is less than two years, and applications with less than two years at the same employer are approved at a lower rate" is meaningful. "The model produced a risk score of 0.73 based on a gradient-boosted ensemble" is not. I've found that the best explanations follow a pattern: the primary factor, the context for why that factor matters, and what the person could do differently. Not every factor is actionable — you can't change your age — but the explanation should focus on the factors that are within the person's control, giving them a path forward rather than just a closed door. ## The Bias Problem Bias in AI isn't a bug — it's a reflection of the data and the society that produced it. Historical hiring data encodes historical discrimination. Historical lending data encodes historical redlining. Historical medical data reflects historical disparities in who received treatment and who didn't. Building an explainable AI system doesn't automatically eliminate bias. But it makes bias visible. When you can see which factors drive decisions and analyze patterns across demographic groups, you can detect bias systematically instead of discovering it through lawsuits or media investigations. The most effective approach I've seen is continuous monitoring — running fairness checks on every model in production, comparing decision rates across protected groups, and flagging disparities for human review. This isn't a one-time audit. Models drift. Data distributions change. A model that was fair at deployment can become biased as the population it serves evolves. ## What We Owe the People on the Other End When I think about AI transparency, I keep coming back to the people who are affected by these systems. The woman whose mortgage was denied. The candidate whose resume was screened out. The patient whose insurance claim was flagged. These are real people with real consequences, and they deserve more than an opaque score. They deserve to know what information was used to make the decision. They deserve to know which factors mattered most. They deserve to know whether the system has been tested for fairness against people like them. They deserve a way to challenge a decision they believe is wrong. Building systems that meet these obligations isn't easy. It requires trade-offs in model design, investments in infrastructure, and a genuine commitment to treating transparency as a feature, not an afterthought. But the alternative — deploying systems that make life-changing decisions about people without any ability to explain why — isn't just technically irresponsible. It's unjust. AI is too powerful to be a black box. The people it affects deserve to see inside. --- ## When Every Customer Needs Their Own Front Door - **Date:** 2026-02-01 - **URL:** https://jwu.computer/posts/when-every-customer-needs-their-own-front-door/ - **Tags:** auth0, multi-tenancy, saas, architecture Imagine you're building an apartment complex. Every tenant gets their own unit with their own lock and their own mailbox. They share the building's infrastructure — the elevators, the plumbing, the electrical system — but they never see inside each other's apartments. A visitor buzzing unit 4B doesn't accidentally end up in unit 7A. The mail carrier doesn't mix up letters between floors. Each resident's experience is private, personal, and isolated from everyone else's. This is the foundational promise of multi-tenant SaaS, and it's much harder to deliver than it sounds. When a hundred companies use your software, each one needs to feel like they're the only customer. Their data is theirs alone. Their users can only see their own workspace. Their configurations don't bleed into anyone else's experience. And if you get this wrong — if one company's employee accidentally sees another company's financial data — you don't just have a bug. You have a trust violation that can end your business. I've built multi-tenant identity systems serving thousands of organizations, and the identity layer is where tenant isolation either succeeds or fails. Everything downstream — authorization, data access, billing, compliance — depends on the authentication system correctly answering one question before all others: which tenant does this user belong to? ## The Three Models of Tenancy There are three fundamental approaches to multi-tenancy in identity systems, and each one represents a different point on the spectrum between isolation and efficiency. ### Shared Infrastructure The most efficient model puts all tenants in a single identity system. Every user, regardless of which organization they belong to, lives in the same database, authenticates through the same endpoints, and is distinguished only by a tenant identifier attached to their account. This is like an apartment building where everyone shares the same front door but gets sorted into their own unit after entering. The advantage is simplicity and cost. You maintain one system, one set of infrastructure, one deployment pipeline. Adding a new tenant is as simple as creating a new identifier. You can serve thousands of tenants with the same resources that would otherwise serve a handful. The risk is that isolation becomes a software concern rather than an infrastructure concern. The only thing preventing Company A's users from seeing Company B's data is the correctness of your application logic. Every database query, every API response, every search result must include a tenant filter. If a single query forgets that filter, data leaks between tenants. I've seen this happen, and the conversations that follow are among the most uncomfortable in enterprise software. ### Isolated Infrastructure The opposite extreme gives each tenant their own dedicated identity system — their own database, their own authentication endpoints, their own configuration. This is like giving each company their own building instead of their own apartment. Isolation is guaranteed by infrastructure, not by code. The advantage is strong isolation. A bug in the application logic can't cause cross-tenant data leakage because there's no shared data to leak. Each tenant can have their own security policies, their own compliance configurations, their own geographic data residency. Enterprise customers, particularly in regulated industries like healthcare and finance, often require this level of isolation. The cost is operational complexity. If you have five hundred tenants, you're managing five hundred identity systems. Each one needs monitoring, patching, backup, and capacity management. Adding a new tenant requires provisioning new infrastructure, which takes minutes or hours instead of milliseconds. Your operations team scales linearly with your tenant count, which is exactly the kind of scaling that SaaS is supposed to avoid. ### The Hybrid Path Most mature SaaS companies end up somewhere in the middle. Smaller tenants share infrastructure — they're price-sensitive, their data volumes are modest, and the efficiency of sharing makes it possible to serve them profitably. Larger tenants, especially those with compliance requirements or enterprise contracts, get dedicated infrastructure — the isolation they need, at the premium they're willing to pay. This hybrid model is elegant in theory and tricky in practice. Your system needs to route authentication requests to the right infrastructure based on the tenant. It needs to manage two different operational models simultaneously. It needs to handle the transition when a tenant grows from the shared tier to the dedicated tier, migrating their data and reconfiguring their access without downtime. I've found that the hybrid model works best when the shared and dedicated paths are implementations of the same interface. The application code doesn't know or care whether a given tenant is on shared or dedicated infrastructure. It authenticates users, checks permissions, and manages sessions through the same abstractions regardless. The routing happens beneath the application layer, invisible to the product code. ## The Tenant Resolution Problem Before your identity system can do anything — authenticate a user, check a permission, return a session — it needs to figure out which tenant is involved. This is tenant resolution, and it happens on every single request. The resolution method depends on how tenants access your application. If each tenant has a custom domain (acme.yourapp.com), the subdomain tells you the tenant. If all tenants share a domain, the tenant might be encoded in the URL path, in a header, or determined during the login flow itself. Some systems ask the user to identify their organization before entering credentials. Others figure it out from the email domain. Each approach has tradeoffs. Custom subdomains are clear and intuitive for users, but they require DNS configuration and certificate management for each tenant. Path-based resolution is simpler to manage but can create confusing URLs. Email-domain resolution is the most natural for users but fails when multiple tenants use the same email domain — a common scenario with large email providers. Whatever method you choose, tenant resolution must be fast and it must be correct. A slow tenant lookup adds latency to every request. An incorrect tenant resolution — sending a user to the wrong tenant — is a security incident. I've seen systems where tenant resolution was implemented as an afterthought, bolted onto the authentication flow with string matching and regular expressions. When those systems scaled, the edge cases multiplied, and the security team spent their time investigating near-misses instead of building features. ## The Token Layer Once a user is authenticated and their tenant is resolved, that information needs to travel with every subsequent request. This is where tokens come in. The access token a user receives after signing in should encode not just who they are, but which tenant they belong to and what they're allowed to do within that tenant. This sounds straightforward, but the details matter enormously. If the tenant identifier in the token can be modified by the client, you have a serious vulnerability — a user could change their tenant ID and gain access to another organization's data. The tenant claim must be set by the server, signed cryptographically, and validated on every request. There's also the question of what happens when a user belongs to multiple tenants. In business-to-business SaaS, this is common — a consultant might have accounts with several client organizations, a partner might access data across multiple tenants. Your token system needs to handle this cleanly, letting the user switch between tenant contexts without confusion or accidental cross-tenant access. I've learned to think of the token as the single source of truth for tenant context. Every downstream service — every API, every database query, every authorization check — derives the tenant from the token, not from any other source. This creates a single point of enforcement: if the token is correct, tenant isolation is maintained throughout the entire request path. ## Onboarding and the First Five Minutes The experience of setting up a new tenant is one of those things that shapes a customer's entire perception of your product. If onboarding is smooth — if a new organization can be created, configured, and ready for their first user within minutes — the product feels professional and well-built. If onboarding requires manual steps, support tickets, or mysterious waiting periods, the product feels immature regardless of how good the underlying technology is. I've seen teams spend months perfecting their authentication system only to have the onboarding experience handled by a manual script that an engineer runs when a new customer signs up. Self-service onboarding — where a customer can create their organization, invite their first users, and configure their identity settings without human intervention — isn't a nice-to-have. It's the foundation of SaaS scalability. The onboarding flow needs to handle identity-specific configuration: which authentication methods the tenant allows, what password policies they want enforced, whether they require single sign-on through their own corporate identity provider, and where their data should be stored geographically. Enterprise tenants often have strong opinions about all of these, and the ability to configure them during onboarding — rather than through a support request after signup — significantly accelerates time-to-value. ## Testing Isolation Here's a truth that keeps me up at night: you can't fully trust tenant isolation until you've tested it adversarially. Writing unit tests that confirm your tenant filter is present in database queries is necessary but not sufficient. You need to actively try to violate isolation — to send requests with manipulated tenant IDs, to access resources across tenant boundaries, to exploit race conditions in tenant resolution — and confirm that every attempt fails. I recommend running isolation tests as part of every deployment pipeline. Before any change reaches production, automated tests should attempt cross-tenant access through every API endpoint, every data access path, and every search interface. If any test succeeds in accessing another tenant's data, the deployment stops. This isn't paranoia. It's the recognition that in a multi-tenant system, the consequences of an isolation failure are uniquely severe. A performance bug degrades experience. A feature bug annoys users. An isolation failure exposes one customer's confidential data to another customer. There's no apology that makes that right. ## The Promise Worth Keeping Multi-tenant architecture is, at its core, a promise: your data is yours, and no one else can see it. Every architectural decision — shared vs. isolated infrastructure, tenant resolution strategy, token design, onboarding flow — serves that promise. The companies that build multi-tenant SaaS well are the ones that take this promise personally. They build isolation into every layer, not just the ones that seem security-critical. They test it continuously, not just when a compliance audit is approaching. They treat every near-miss as a serious incident, not a minor bug. Because for the customer on the other side of the login page, the promise of isolation isn't an architectural detail. It's the reason they trusted you with their data in the first place. --- ## The Morning After: Changing Every Lock While the Building Is Still Occupied - **Date:** 2026-01-30 - **URL:** https://jwu.computer/posts/the-morning-after-changing-every-lock-while-the-building-is-still-occupied/ - **Tags:** 2026, Security, Engineering The call comes in at 6:47 AM on a Tuesday. Your security team has confirmed that credentials have been compromised. Maybe it's database passwords found in a leaked repository. Maybe it's API keys discovered in a threat actor's toolkit. Maybe it's the aftermath of a breach like Snowflake's, and you're one of the 165 companies scrambling to respond. Whatever the trigger, the clock is now running, and you need to change every lock in the building — while the building is full of people trying to do their jobs. I've been through this more times than I'd like to admit. The first time, it was chaos. We broke production three times in the first hour. By the fourth or fifth rotation, we had it down to a disciplined procedure. This is what I've learned about the art and science of credential rotation under pressure. ## Why You Can't Just Flip a Switch The instinct in a breach is to revoke everything immediately. Change every password. Rotate every key. Shut it all down. That instinct is understandable, and it's wrong. If you instantly revoke a database password that twenty microservices are using, you don't just lock out the attacker. You lock out your own applications. Your customers see errors. Your monitoring lights up. And now you're fighting two fires instead of one — the breach and the outage you just caused. The fundamental challenge of credential rotation is that you're performing surgery on a running system. Every credential connects two things: a producer that validates it and a consumer that presents it. Between those two things is live traffic, real users, real revenue. You have to swap the credential without dropping that connection, even for a moment. This is why credential rotation needs to be practiced before you need it. Under the pressure of an active breach, with your CEO asking for updates every fifteen minutes and your legal team drafting notification letters, you don't want to be figuring out the procedure for the first time. ## The Order of Operations The single most important thing I've learned about credential rotation is that order matters enormously. Get it wrong and you create cascading failures. Get it right and it's almost boring — which is exactly what you want during an incident. ### Phase One: Assessment Before you change anything, you need to know what you're changing. This sounds obvious, but in the heat of an incident, teams often start rotating the credentials they know about while forgetting the ones they don't. The first step is building a complete inventory. Every compromised credential needs to be mapped to every system that produces it and every system that consumes it. In a typical organization, a single database password might be referenced in a secrets manager, hardcoded in a legacy configuration file, embedded in three Kubernetes deployments, and stored in a CI/CD pipeline's environment variables. Miss any one of those, and your rotation breaks something. I've seen teams create this inventory under pressure on whiteboards, in spreadsheets, in shared documents. The format doesn't matter. What matters is that someone owns the list and it's treated as the single source of truth for the entire rotation effort. The assessment also needs to determine the order of rotation. Not all credentials are equal. Some are being actively exploited — those go first. Some grant broader access than others — those are higher priority. Some are shared by critical-path services that can't tolerate any downtime — those need the most careful handling. ### Phase Two: Dual Credentials This is the key insight that separates a smooth rotation from a disaster: before you remove the old credential, you need to make the system accept both the old and the new one simultaneously. Think of it like rekeying a house. You don't change the lock while someone's trying to open the door. You install a new lock that accepts both the old key and the new one. Then you hand out new keys to everyone. Then, once you've confirmed everyone has the new key, you remove the old one from the lock. In practice, this means updating the producer — the system that validates the credential — to accept two valid values. For a database, that might mean creating a new role with a new password while keeping the old role active. For an API key, it means adding the new key to the valid key set before removing the old one. For JWT signing keys, it means publishing both keys in your key set so tokens signed with either key will verify successfully. The dual-credential window is uncomfortable. You know the old credential is compromised, and you're deliberately keeping it valid. But the alternative — invalidating it before all consumers have switched — is worse. A brief period of controlled risk is better than an uncontrolled outage. ### Phase Three: Consumer Updates Once the producer accepts both credentials, you start updating consumers — the services that use the credential to authenticate. This is the longest phase and the one most likely to surface surprises. The update order matters here too. Start with the services closest to the breach. If database credentials were stolen, update the services that have direct database access first. Then move to services that depend on those services. Work outward from the blast radius. Within each tier, update services in order of criticality. Your payment processing system goes before your internal analytics dashboard. Customer-facing APIs go before background batch jobs. The reasoning is simple: if something goes wrong with the rotation, you want it to go wrong on the system where the impact is smallest and most recoverable. The most treacherous part of consumer updates is the services you forgot about. The cron job that runs once a month and has the old credential hardcoded. The partner integration that was set up two years ago by someone who's since left the company. The monitoring system that authenticates with the same credentials as the application it's monitoring. Every organization has these hidden consumers, and they always surface at the worst possible time. ### Phase Four: Verification and Cleanup After all consumers are updated, you enter the verification window. This is the period where you watch and wait, confirming that no traffic is still using the old credential before you revoke it. The length of this window depends on the type of credential. For a database password, a few hours might be enough. For a JWT signing key, you need to wait at least as long as the token lifetime — if your tokens are valid for fifteen minutes, you need to keep the old signing key active for at least that long after the last token was issued with it. During verification, you're looking for two things: proof that the new credential is being used by all consumers, and proof that the old credential is not being used by any legitimate traffic. If you see any legitimate requests with the old credential, you have a straggler — a consumer you missed in Phase Three. Find it, update it, and restart the verification window. Only after the verification window closes with zero legitimate old-credential traffic do you revoke the old credential. This is the point of no return. Any consumer still using the old credential will break. That's why the verification is so important. ## The Credentials You Dread Most Not all credentials are created equal when it comes to rotation difficulty. Some are routine. Others keep you awake. Database passwords are the ones I dread most. Get the rotation wrong and you have an immediate production outage — every query fails, every transaction hangs. The dual-credential approach is essential here, and it needs to be tested in a staging environment before you attempt it in production. Signing keys — the cryptographic keys used to sign JWTs, API requests, and certificates — are a special case because they're temporal. A JWT signed with the old key might still be valid and in use. You can't just swap keys; you need to overlap them. Both keys need to coexist until every token signed with the old key has naturally expired. Cloud provider credentials — AWS access keys, GCP service account keys — are dangerous because they often grant broad access and are embedded in places you don't expect. That Lambda function someone deployed two years ago. That CI/CD pipeline that runs on a schedule. That Terraform state file sitting in a storage bucket. Third-party API keys are frustrating for a different reason: you often can't control how fast the provider rotates on their end. Some providers support multiple active keys, which makes rotation smooth. Others require you to invalidate the old key before creating a new one, which creates an unavoidable gap. ## Building the Muscle Before You Need It The teams that handle credential rotation well are the ones that practice it regularly. They run rotation drills quarterly — not because they've been breached, but because they want the procedure to be routine. During a drill, you learn things that no amount of documentation can teach you. You discover that one service takes forty-five minutes to pick up a new credential because it caches aggressively. You find out that your secrets manager has an API rate limit that you hit when rotating twenty credentials simultaneously. You realize that your monitoring alerts fire when credentials change, creating noise that could mask real problems during an actual incident. You also build the cultural muscle of rotation. People get comfortable with the process. They know their role. They know the communication channels. They know what the dashboards should look like at each phase. When the real incident comes, they execute the procedure from muscle memory instead of panic. I've also learned the value of keeping a runbook — a step-by-step document that covers every phase of rotation for every type of credential in your system. The runbook includes not just the steps, but the verification criteria for each step and the rollback procedure if something goes wrong. Under the pressure of an active breach, a good runbook is worth more than any technology. ## The Real Goal The goal of credential rotation isn't to make it painless. It's to make it practiced, predictable, and fast enough that the window of exposure is as small as possible. Every hour between "credential compromised" and "credential rotated" is an hour the attacker can use the stolen credential. Every minute of unnecessary downtime during rotation is a minute of impact on your customers. The discipline of credential rotation is about compressing both of those windows — moving fast without breaking things. The best rotation I ever participated in took forty-five minutes from detection to full rotation of all affected credentials, with zero customer impact. It was smooth not because we were brilliant in the moment, but because we had practiced the exact same procedure three times that year. When the real call came, we already knew what to do. That's the secret nobody tells you about incident response: the heroic moments are built on boring rehearsals. --- ## Problem-first thinking - **Date:** 2026-01-28 - **URL:** https://jwu.computer/posts/problem-first-thinking/ - **Tags:** 2026, Engineering, Leadership, Problem-Solving, Process Over the past few years as a staff engineer — building privacy systems at scale and maintaining a CIAM platform serving millions of users — I've watched smart engineers waste months of work on the wrong things. Not because they lacked talent, but because they skipped the most fundamental step in engineering: {{< callout >}} Define the problem. {{< /callout >}} This post is about a mindset I call **Problem Thinking**. It's not a novel idea. It borrows from the [Toyota Production System's 5 Whys](https://en.wikipedia.org/wiki/Five_whys), from the [RFC process](https://www.rfc-editor.org/rfc/rfc2026), and from the hard lessons that come with operating large-scale distributed systems. But the simplicity of the idea is exactly what makes it so easy to skip — and so costly to ignore. ## Solution-first thinking Engineers love building things. That instinct is what makes us good at our jobs, and it's also our biggest liability. When we see a system that isn't good enough, our reflex is to reach for a solution. We sketch architectures, prototype tools, pitch improvements. The problem is that we often do all of this before we've confirmed what the actual problem is. Here's a real example. My team was responsible for the hero banner system on a large e-commerce storefront. We knew the system had reliability issues. So we proposed building a high-performance distributed [LRU cache](https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU) to ensure high availability and extreme reliability. On paper it looked like a solid engineering investment. But we hadn't clearly defined the stakeholder or the pain point. Two facts changed everything. First, the hero banner — while visible — didn't block the customer journey or purchasing flow. A brief degradation wasn't a revenue-impacting event. Second, the hero banner data was owned by the external ads team. We only provided the entry point to expose their content. Aggressive caching on our side would directly interfere with ads rotation, impacting ads revenue and pricing accuracy. We were optimizing for the wrong thing. The problem wasn't cache performance. The problem was that we hadn't asked who was affected, what they actually needed, and what the downstream consequences of our solution would be. ## The core idea Problem thinking comes down to three principles. 1. You have more ideas than resources. Every team has a backlog of things that aren't good enough — systems that could be faster, processes that could be cleaner. But resources are finite. You can only work on what's most urgent and what your customers or stakeholders need the most. 2. You need to identify which pain point has the highest Return on Investment (ROI). Among all the things that hurt, which one matters most? That question sounds simple, but the answer is rarely obvious. 3. This is the critical part — **what you think is the problem is often just a consequence of another problem**. The symptom presents itself clearly. The root cause hides behind it. If you solve the symptom, you'll be back in six months solving it again. Or worse, you'll have created a new problem in the process. ## 5 Whys: the mechanism The technique for getting past symptoms to root causes isn't complicated. You keep asking why. This comes directly from the [Toyota Production System](https://en.wikipedia.org/wiki/Toyota_Production_System), where it was developed to diagnose manufacturing defects. It works just as well in software. The goal isn't to ask "why" exactly five times. The goal is to keep asking until the reasoning chain is clear and every person in the room is on the same page. In my experience, you're done when you can state the problem in a way that everyone agrees with and no one can poke a hole in. At my previous company, this was the standard for senior engineers. When you presented a proposal or went through a design review, the senior staff engineers would focus on the problem statement first. They'd keep asking why — not to be difficult, but to evaluate whether the pain point was well defined. If you couldn't articulate the problem clearly, you weren't ready to propose a solution. Everyone on a team generally knows the pain points from empirical data and experience. But there's a difference between feeling that something is wrong and being able to describe the scope and statement with the right words and a complete reasoning chain. That articulation is the job of the senior engineer, and it's one of the highest-leverage things they do. In practice, it looks like this: someone starts a conversation with a solution, and you stop them. What problem are you solving? How do you know it's a problem? Who told you, and what evidence supports it? What happens if you solve nothing? These aren't hostile questions — they're the cheapest form of engineering due diligence. ## RFCs are problem thinking in document form I see a direct connection between problem thinking and the [Request for Comments (RFC)](https://en.wikipedia.org/wiki/Request_for_Comments) process. An RFC isn't just documentation of a solution. It's a mechanism for iterative problem refinement. After you run the 5 Whys and identify what you believe the root problem is, you still need proof. You need technical details that precisely describe the gap between the current state and the desired state. That process requires significant back-and-forth between team members and stakeholders. People see things differently. They have context you don't. The RFC formalizes that iteration. You're literally requesting comments, and those comments sharpen your understanding of the problem before you commit to a solution. The best RFCs I've read don't start with "we should build X." They start with "here's what's broken, here's the evidence, here's who it affects, and here's why it matters now." ## Avoiding analysis paralysis A fair objection to all of this is that you can spend forever defining a problem and never ship anything. Problem thinking isn't an excuse for inaction — and it shouldn't become [analysis paralysis](https://en.wikipedia.org/wiki/Analysis_paralysis). The boundary is practical. Once you have a clear problem statement and a reasoning chain that holds up, figure out what you can build in two weeks to test your understanding. Ship the [MVP](https://en.wikipedia.org/wiki/Minimum_viable_product). The rest is feedback. You verify your problem definition by putting something in front of users and stakeholders — not by debating in a conference room until the problem is perfectly defined on a whiteboard. ## Beyond engineering I've found that this framework applies well outside of code. When I was weighing a major career decision, I kept asking myself foundational questions. Why make this move? What's the actual problem I'm trying to solve — compensation, growth, impact, something else? I kept asking until the reasoning was clear. More importantly, the process forced me to answer the biggest question: what do I want the next ten years of my life to look like? Problem thinking scales with scope. A junior engineer defines problems within the boundary of their team, and a local maxima is generally fine at that level. A tech lead or staff engineer has to consider cross-team dependencies, organizational trade-offs, and second-order effects. The framework is the same. The blast radius is what changes. You can't solve a problem you haven't defined. Every hour spent clarifying the problem saves ten hours building the wrong thing. --- ## Your Customers Are Not Your Employees - **Date:** 2026-01-24 - **URL:** https://jwu.computer/posts/your-customers-are-not-your-employees/ - **Tags:** ciam, iam, identity, security I've watched the same story unfold at three different companies. A growing startup needs a customer login system. They already have an employee identity system — Active Directory, Okta, or something similar — that works well. Someone suggests building customer authentication on top of it. It seems logical. Identity is identity, right? You have users, they have credentials, you verify them and let them in. How different can it be? Eighteen months later, the team is deep in a rewrite. The employee identity system that managed two thousand accounts beautifully is buckling under two million customer accounts. Performance is degrading. The user experience is frustrating customers. The security model is either too rigid for consumers or too loose for the data being protected. The company is spending more engineering time working around the limitations of their identity system than building the product their customers actually want. The reason this keeps happening is that employee identity and customer identity look similar on the surface but are fundamentally different problems. Understanding why — understanding the deep structural differences between the people you hire and the people you serve — is the key to building an identity system that doesn't need to be rewritten. ## People You Know vs. People You Don't When you hire an employee, you know who they are before they ever touch your systems. They went through an interview process. They provided identification. They signed an employment agreement. HR created their account. IT provisioned their laptop. By the time they first sign in, their identity has been verified through multiple channels, and their relationship with your organization is formally documented. When a customer creates an account, you know almost nothing about them. They provide an email address — which might be valid, might not. They pick a password — which might be strong, might be "password123." They might give you a name, but you have no way to verify it's their real one. Your entire relationship with this person starts with an act of trust: you trust that they are who they claim to be, and they trust that you'll protect whatever information they give you. This asymmetry changes everything about how identity systems need to work. Employee identity is built on verification. Customer identity is built on trust. Employee identity starts with certainty and maintains it. Customer identity starts with uncertainty and gradually builds confidence over time, through email confirmations, phone verifications, payment methods, and behavioral patterns. ## The Scale Cliff The most obvious difference is scale, but it's not just about bigger numbers. It's about a fundamentally different relationship between your system and its users. An employee identity system manages a bounded population. Even the world's largest employers have fewer than three million employees. Growth is predictable — you know roughly how many people you'll hire next quarter. The system can be sized accordingly, with comfortable margins. Customer identity is unbounded. A viral marketing campaign can add a million accounts in a weekend. A partnership announcement can double your user base overnight. A competitor's outage can push their entire customer base to your registration page in the span of an afternoon. Your identity system needs to handle not just current volume but sudden, unpredictable spikes — and it needs to handle them gracefully, because every failed registration is a lost customer. I've seen the scale cliff in action. A system designed for steady-state employee access gets repurposed for customer-facing use. It handles the first hundred thousand customers fine. At a million, logins start taking two seconds instead of two hundred milliseconds. At five million, the authentication database's connection pool is saturated during peak hours. By ten million, the team is firefighting daily, and every sprint is consumed by identity infrastructure instead of product features. ## The Friction Equation For employees, friction is acceptable. Your company requires a complex password, a hardware security key, and a VPN connection? Fine. Employees do it because it's their job. They've been trained. There's an IT help desk if something goes wrong. The cost of friction is mild annoyance, not lost revenue. For customers, friction is fatal. Every additional step in your login or registration flow has a measurable cost. Research consistently shows that each additional second of login latency reduces conversion. Each extra field in a registration form decreases completion rates. A password requirement that's too strict drives people to use password reset as their primary login method — or to abandon your service entirely. This creates a genuine tension between security and usability that doesn't exist in the employee context. Your security team wants strong passwords, frequent rotation, and multi-factor authentication. Your product team wants one-click sign-in, social login, and the fewest possible barriers between a person and your product. Both are right, and reconciling their requirements is the core challenge of customer identity. The resolution isn't to sacrifice one for the other. It's to be strategic about where friction appears. Sign-up should be almost frictionless — get people in, build the relationship, add security progressively. The first login should be as easy as possible. Security challenges should escalate gradually based on risk: a routine login from a known device gets no additional friction, while a login from a new country on a new device might require additional verification. ## The Lifecycle Difference Employee identity has a clear lifecycle with well-defined transitions. An employee is hired (onboarding), works at the company (active), changes roles (modification), and eventually leaves (offboarding). Each transition is triggered by a deliberate business process. Someone in HR initiates it. There's a formal workflow. When an employee leaves, their access is revoked — ideally on their last day, though many organizations are disturbingly slow about this. Customer identity has a fuzzy, self-directed lifecycle that the business can influence but not control. A customer might create an account and never return. They might use your service daily for a year and then go dormant for six months. They might create multiple accounts with different email addresses, not realizing they already have one. They might share their login with family members. They might want to delete their account entirely and expect you to erase every trace of their existence — a right that regulations like GDPR and CCPA have codified into law. This self-directed nature means customer identity systems need to handle states that employee identity systems never encounter. Dormant accounts that might reactivate at any time. Merge requests when someone realizes they have two accounts. Deletion requests that need to cascade through every system that ever touched that user's data. Progressive profile completion where a user provides information over months or years, not all at once during a structured onboarding process. ## The Security Model Divergence Employee identity security is built around a clear trust boundary. Everyone inside the boundary is, to some degree, trusted. They've been vetted. They've signed policies. They can be held accountable. The security model can assume a baseline of good faith and focus on preventing mistakes and detecting anomalies. Customer identity security must assume adversarial conditions. Some percentage of your authentication traffic is credential stuffing attacks — automated attempts to sign in using stolen username-password combinations from other breaches. Some percentage of account registrations are bots. Some users will try to abuse your system in ways you haven't anticipated. This means customer identity systems need defenses that would be overkill in an employee context. Sophisticated bot detection during registration. Rate limiting that can distinguish between a legitimate user who mistyped their password and an attacker working through a list of stolen credentials. Anomaly detection that notices when an account's behavior suddenly changes — a new device, a new country, a new pattern — and challenges appropriately. ## The Build vs. Buy Decision Many companies start by building their customer identity in-house. It seems simple enough — a users table, a login endpoint, password hashing. But identity has a way of becoming the most complex part of your application. Password reset flows, email verification, social login integration, multi-factor authentication, rate limiting, bot detection, session management, token handling, GDPR compliance, CCPA compliance — the feature list grows relentlessly, and every feature is security-critical. I've come to believe that for most companies, the right answer is to buy or adopt a purpose-built CIAM platform and invest engineering time in integrating it well, rather than building it from scratch. The companies that build identity platforms as their core business — Auth0, Firebase, Cognito, and others — have teams dedicated to nothing else. They've seen every edge case, handled every attack pattern, and navigated every compliance requirement. The exception is when your identity requirements are genuinely novel — when the way your customers authenticate is so unique to your business that no existing platform can accommodate it. But in my experience, most companies overestimate how unique their requirements are. The fundamentals of customer identity are remarkably consistent across industries. ## The Rewrite That Didn't Have to Happen The three rewrites I mentioned at the start all followed the same pattern: someone assumed that identity is identity, built customer authentication on employee infrastructure, and eventually hit the wall. The scale wall, the friction wall, the security wall, or all three at once. The lesson isn't that employee identity systems are bad. They're excellent at what they're designed for. The lesson is that customer identity is a fundamentally different problem — different scale, different expectations, different security model, different lifecycle. The people using your consumer application have different needs, different behaviors, and different rights than the people on your payroll. Respecting that difference from the beginning — choosing tools and architectures designed for the customer context — saves the year-long rewrite that otherwise becomes inevitable. And more importantly, it gives your customers the experience they deserve: fast, frictionless, and secure in ways they never have to think about. --- ## The Invisible Architecture Behind Every Login - **Date:** 2026-01-10 - **URL:** https://jwu.computer/posts/the-invisible-architecture-behind-every-login/ - **Tags:** ciam, identity, architecture, engineering Every time you tap "Sign in" on your phone, something remarkable happens in the space between your thumb leaving the screen and the app opening. A system somewhere in the world receives your credentials, finds your account among tens of millions of others, verifies your identity, creates a session, checks your permissions, and sends back a response — all in less time than it takes you to blink. You never think about it. That's the point. The best identity systems are invisible. When they work, the person on the other end feels nothing except a seamless transition from "not signed in" to "signed in." When they don't work — when the login is slow, or the session drops, or the password reset email never arrives — the frustration is immediate and personal. I've spent years building these systems. Customer identity architecture, or CIAM, is one of those fields that seems straightforward until you're responsible for it at scale. Managing ten thousand accounts is a spreadsheet problem. Managing fifty million accounts across three continents with sub-second response times is an engineering discipline unto itself. ## Why Customer Identity Is Different The first thing to understand about customer identity is that it's fundamentally different from employee identity. When a company manages access for its own employees, the rules are relatively simple. You know who everyone is. You hired them. You gave them a badge and a laptop. If they forget their password, they walk down the hall to IT. The scale is bounded — even the largest companies have maybe a few hundred thousand employees. Customer identity is a different universe. Your users are strangers. You don't know who they are until they tell you, and even then you're trusting their self-reported information. They come from every country, speak every language, use every device and browser combination imaginable. They show up at unpredictable times in unpredictable volumes. And if they hit any friction — a slow login, a confusing password requirement, a broken reset flow — they leave. Not to complain, just to leave. Every second of delay at your login page is a measurable drop in conversion. This is the tension at the heart of CIAM: you need to be fast and frictionless for the user, but rigorous and secure for the business. Those goals aren't opposed, but they require careful architecture to achieve together. ## The Identity Store Problem At the foundation of any customer identity system is the identity store — the database that holds your users' accounts. At small scale, this is a table in PostgreSQL. At large scale, it's the most carefully engineered component in your entire stack. The challenge is that identity lookups need to be simultaneously fast and flexible. When someone signs in, you need to find their account by email address, phone number, username, or social provider ID. That's at least four different lookup paths, each of which needs to return results in single-digit milliseconds. As your user base grows, maintaining that speed requires thoughtful indexing, read replicas, and often a caching layer that sits between your application and your database. But speed isn't the only concern. Identity data is among the most sensitive information your system holds. Passwords must be hashed with modern algorithms that are deliberately slow — a security measure that directly conflicts with the performance requirement. Personal information must be encrypted at rest and in transit. Access to the identity store must be tightly controlled and audited. You're simultaneously optimizing for speed and security, and every decision involves a tradeoff. I've seen organizations stumble when they treat the identity store like any other database. They put it behind the same connection pool as their product data, share the same monitoring, apply the same scaling rules. Identity data has different access patterns — it's read-heavy, latency-sensitive, and mission-critical. It deserves its own infrastructure, its own scaling strategy, and its own team watching the dashboards. ## Sessions and the Illusion of Continuity When you sign in to an application and navigate between pages without signing in again, that continuity is an illusion maintained by the session layer. Your browser holds a token — a cryptographic proof that you authenticated successfully at some point in the recent past — and presents it with every request. The system validates that token, confirms it hasn't expired or been revoked, and lets you through. This happens hundreds of times during a single browsing session, and each validation needs to be fast. At scale, session management becomes surprisingly complex. If your application runs across multiple data centers, every data center needs to know about every valid session. A user who signs in via a server in Virginia and then loads a page served from Frankfurt shouldn't be asked to sign in again. This means either replicating session data globally — which introduces consistency challenges — or using self-contained tokens that carry their own validity proof, which introduces size and revocation challenges. There's also the question of what happens when a session needs to end. If you detect that a user's account has been compromised, you need to invalidate all their sessions immediately, everywhere. If your sessions are stored in a central database, that's straightforward. If they're encoded as self-contained tokens distributed across CDN edges, it's much harder. This is one of those design decisions that seems minor when you have a thousand users and becomes critical when you have ten million. ## The Geography of Trust Modern customer identity systems need to work globally, and that introduces challenges that go beyond latency. Different countries have different laws about where personal data can be stored. The European Union's General Data Protection Regulation requires that data about EU residents be handled with specific protections, and in some interpretations, stored within EU borders. Similar regulations exist in Brazil, India, Japan, and dozens of other jurisdictions. This means a global CIAM system often needs to be a federation of regional systems. A user in Berlin has their identity data stored in an EU data center. A user in Tokyo has theirs in an Asian data center. But they're both customers of the same application, which needs to provide a consistent experience regardless of where the user is. The engineering challenge is routing each authentication request to the right regional system, maintaining cross-region consistency for users who travel, and enforcing data residency requirements without adding latency for the user. I've seen teams spend months on this problem, and the right solution depends entirely on the regulatory requirements and the acceptable tradeoff between consistency and speed. ## When Everyone Shows Up at Once Authentication traffic is spiky. On a normal Tuesday, your system handles a steady load. Then a marketing campaign sends an email to five million users at 9 AM, and your login endpoint sees a ten-times traffic spike in thirty seconds. Or a popular product launch drives millions of people to create accounts simultaneously. Or a competitor's outage pushes their users to your platform in a wave. Rate limiting and capacity planning for identity systems require a different mindset than for most application services. You can't just drop excess authentication requests — each dropped request is a person who can't sign in to your product. But you also can't let unlimited traffic through, because that's how denial-of-service attacks overwhelm your systems. The approach I've found most effective is layered defense. At the outermost layer, you block obviously abusive traffic — requests from known attack infrastructure, requests that arrive faster than any human could type. At the next layer, you implement progressive challenges — if someone fails a password check three times, you might require a CAPTCHA or introduce a delay before allowing another attempt. At the innermost layer, you scale your authentication infrastructure independently of your application infrastructure, with dedicated capacity reserved for authentication that can't be consumed by other workloads. ## The Observability Gap Here's something that surprised me early in my career: most organizations have detailed monitoring for their application performance — response times, error rates, throughput — but almost no visibility into their identity system's behavior. They can tell you how long a product page takes to load, but they can't tell you how long a typical login takes, or what percentage of password reset emails are never opened, or how many users abandon the sign-up flow at the email verification step. These are identity-specific metrics, and they matter enormously. A two-second login delay might correlate with a measurable drop in daily active users. A confusing password requirement might be driving away a specific demographic. I've learned to instrument every step of the identity lifecycle: registration, authentication, password reset, session creation, session renewal, and account deletion. Each step gets its own metrics for latency, success rate, and error distribution. When something goes wrong — and with millions of users, something is always going wrong somewhere — these metrics tell you what's happening and for whom. ## What Good Architecture Feels Like The best customer identity architecture I've ever worked with shared a few characteristics. It was boring. It was predictable. Logins were fast, password resets worked, sessions stayed alive, and nobody thought about it. Behind the scenes, it was anything but simple. There were read replicas strategically placed near major user populations. There was a caching layer that reduced identity store lookups by 90%. There was a session system that handled global consistency without noticeable latency. There was a rate limiter that could absorb traffic spikes without dropping legitimate users. There was monitoring that caught issues before users noticed them. All of that complexity existed for a single purpose: to make the experience invisible. The person tapping "Sign in" on their phone never knew or cared about any of it. They just knew that the app opened, and they were themselves, and everything was where they left it. That's what good identity architecture feels like from the outside: nothing at all. And that's what makes it worth building. --- ## When Optional Security Fails 165 Companies at Once - **Date:** 2026-01-08 - **URL:** https://jwu.computer/posts/when-optional-security-fails-165-companies-at-once/ - **Tags:** security, breach, incident-response, identity In the spring of 2024, something happened that should have been impossible. Over 165 companies — Ticketmaster, AT&T, Santander Bank, Advance Auto Parts, and dozens more — had their customer data stolen in a single coordinated campaign. Hundreds of millions of people had their personal information exposed. Not because of some brilliant zero-day exploit. Not because a nation-state hacker found a flaw in Snowflake's cloud infrastructure. But because attackers walked in through the front door using stolen passwords, and nobody had turned on multi-factor authentication. I remember reading the Mandiant report when it came out and feeling a familiar mix of frustration and sadness. The breach wasn't sophisticated. That's the tragedy. It was the most predictable kind of failure — the kind that happens when security is offered as an option rather than enforced as a standard. ## The Story of What Actually Happened The attackers didn't need to be clever. They had a collection of usernames and passwords — some stolen from previous breaches years earlier, some harvested by infostealer malware sitting quietly on employee laptops. They tried these credentials against Snowflake customer accounts, one by one. When the password worked and no second factor was required, they were in. It was that simple. What made the breach so devastating wasn't any single point of failure. It was the compounding of several reasonable-sounding decisions. Snowflake had built multi-factor authentication into their platform. It was available. It worked. But it was optional. Each customer could decide for themselves whether to require it. And in 165 cases, the answer was no — not because anyone consciously chose to be insecure, but because enabling MFA across an organization is friction, and friction gets deprioritized. I've seen this pattern so many times in my career. A security team builds a feature that would prevent a breach. The product team makes it optional to avoid disrupting existing workflows. The customer security teams mean to enable it eventually but never get around to it. And then one morning the breach happens, and everyone asks how it could have been prevented — when the prevention was already built and sitting unused. The timeline tells the human story. The attackers began probing accounts in April 2024. They operated for weeks before anyone noticed. By the time Snowflake issued their advisory in late May, the data had already been exfiltrated from dozens of accounts. Some companies didn't realize they were affected until they saw their customer data for sale on criminal forums. Think about what that means for the people whose data was stolen. A parent who bought concert tickets on Ticketmaster. A retiree with an AT&T phone plan. A small business owner who bought auto parts online. None of them had any relationship with Snowflake. They'd never heard of the platform. But their personal information — names, phone numbers, social security numbers, financial details — was now in the hands of criminals, because a data warehouse they'd never interacted with didn't enforce a security feature that would have stopped the entire attack. ## Five Lessons Written in Other People's Data ### Lesson One: Security Cannot Be Optional The most important lesson from the Snowflake breach is the simplest one: security features that protect other people's data cannot be left to individual choice. When Snowflake made MFA optional, they weren't just giving their customers flexibility. They were making a decision about the security of their customers' customers — the hundreds of millions of people whose data flowed through the platform. This is the fundamental tension in business-to-business technology. The company that buys the software isn't the only one affected by its security posture. The people whose data is stored in that software have no voice in whether MFA gets enabled. They can't opt in to better protection. They're entirely dependent on decisions made in meetings they'll never know about. I believe the industry needs to move past the idea that strong authentication is a premium feature or an optional configuration. For any system that stores personal data at scale, MFA should be the default — not something you turn on, but something you'd have to deliberately turn off with full understanding of the consequences. ### Lesson Two: Old Passwords Never Die Many of the credentials used in the Snowflake attack were years old. Some had been stolen in breaches dating back to 2020 or earlier. They still worked because nobody had forced a password change, and the users had reused the same password across multiple services. This is a deeply human problem. People have too many accounts and too many passwords. They reuse them not because they're careless, but because the cognitive load of managing hundreds of unique passwords is genuinely unreasonable. We've built a system that expects superhuman behavior from ordinary people, and then we blame them when they act like humans. The engineering response to this can't just be "use a password manager." That helps, but it doesn't solve the systemic problem. What works is credential monitoring — actively checking whether your users' passwords have appeared in known breach databases and forcing a reset when they have. Services like Have I Been Pwned provide this capability. Integrating it into your authentication pipeline means catching compromised credentials before attackers can use them, rather than after. ### Lesson Three: If You Can't See It, You Can't Stop It The Snowflake attackers operated for weeks. They logged in, queried data, and exfiltrated it — all using normal-looking API calls. There were no alarms because the systems weren't looking for the right things. Detection isn't just about monitoring for malware or network intrusions. It's about understanding what normal behavior looks like and noticing when something deviates. A single user account suddenly downloading the entire contents of a data warehouse at 3 AM is not normal, no matter how valid the credentials are. But if you're not tracking data access patterns, volume anomalies, and geographic inconsistencies, you'll never see it. I've worked on teams that invested heavily in perimeter security but barely monitored what happened after authentication. That's like installing an excellent lock on your front door but having no idea who's walking around inside your house. The Snowflake breach proved that post-authentication monitoring is just as important as the authentication itself. ### Lesson Four: Defense in Depth Isn't a Buzzword Every security professional learns about defense in depth — the idea that you need multiple layers of protection so that no single failure is catastrophic. The Snowflake breach is what happens when you have exactly one layer: the password. If MFA had been required, the stolen passwords wouldn't have been enough. If credential monitoring had been in place, the reused passwords would have been flagged. If data access anomaly detection had been running, the mass exfiltration would have triggered alerts. If network policies had restricted access to known IP ranges, the attackers' infrastructure would have been blocked. Any one of these layers would have significantly limited the damage. Together, they would have prevented the breach entirely. The lesson isn't that you need every possible security control. It's that you need enough layers that the failure of any single one doesn't leave you completely exposed. When I audit identity systems now, I count the layers between "attacker has a stolen password" and "attacker has exfiltrated customer data." If that number is one, we have work to do. ### Lesson Five: Incident Response Is a Muscle The companies that handled the Snowflake breach best were the ones that had practiced their incident response before they needed it. They had runbooks. They had communication plans. They knew who to call, what to shut down first, and how to communicate with affected customers. The companies that handled it worst were the ones for whom this was their first real security incident. They scrambled. They made conflicting public statements. Some waited weeks to notify affected customers, hoping the problem would somehow resolve itself. Incident response is like a fire drill. Nobody enjoys doing it. It feels like wasted time when everything is running smoothly. But when the fire actually happens, the difference between a team that has drilled and a team that hasn't is the difference between an orderly evacuation and a panic. ## What This Means for the Rest of Us The Snowflake breach is a case study in what happens when we treat security as someone else's problem. Snowflake built the MFA feature but didn't enforce it. Their customers meant to enable it but didn't prioritize it. And the people whose data was stolen had no say in any of these decisions. I keep coming back to those people — the ones whose personal information was exposed because of a chain of reasonable-seeming choices that added up to an unreasonable outcome. A parent who just wanted to buy concert tickets. A retiree who just wanted a phone plan. We build systems that hold the most intimate details of people's lives, and we owe those people more than optional security. We owe them systems that are secure by default, that assume credentials will be stolen and plan accordingly, that watch for the signs of compromise even when the front door credentials check out. The Snowflake breach wasn't inevitable. Every single thing that went wrong had a known solution. The failure wasn't technical — it was a failure of will, of prioritization, of taking the easy path when the data at stake belonged to someone else. That's the lesson that keeps me up at night. Not the sophisticated attacks. The simple ones that we already know how to prevent, and don't. --- ## Why passwords were always the wrong answer - **Date:** 2023-03-08 - **URL:** https://jwu.computer/posts/why-passwords-were-always-the-wrong-answer/ - **Tags:** 2025, Auth0, Passwordless, Authentication, Security, Awesome IAM My mother has a notebook in her kitchen drawer. It's labeled "passwords" in blue ink. Inside, in careful handwriting, are the login credentials for every service she uses — her email, her bank, her streaming accounts, her pharmacy. She knows this isn't secure. She's been told a hundred times not to write passwords down. But she has forty-three accounts across various services, and she's seventy-two years old, and she'd rather have a notebook in a drawer than be locked out of her own life. She's not the problem. The system that demands she memorize forty-three unique strings of letters, numbers, and symbols is the problem. Passwords are the worst authentication mechanism we've ever normalized. They're forgotten constantly, reused everywhere, phished routinely, and stolen at an industrial scale. The average person has to manage far more passwords than human memory can reasonably handle, and when they inevitably fail at this impossible task, we call it a security weakness instead of a design failure. I've spent the last several years working on alternatives — magic links, passkeys, biometric authentication — and I'm convinced that the shift away from passwords isn't just a technical improvement. It's an act of respect toward the people who use our systems. ## The case against passwords The numbers tell a damning story. Over 80% of data breaches involve stolen or weak credentials. The average person reuses passwords across at least four or five services, which means a breach at any one of them compromises all of them. Password reset is the single most-used authentication flow at most consumer services — not because people want to reset their passwords, but because they can't remember them. We've tried to fix passwords by making them more complex. Minimum eight characters. Must include uppercase, lowercase, numbers, and special characters. Can't be the same as your last five passwords. Must be changed every ninety days. These requirements don't make users more secure. They make users more creative at working around the requirements — "Password1!" meets every complexity rule and protects nothing. The fundamental flaw isn't in how passwords are implemented. It's in the concept itself. A password is a shared secret — something that both you and the system know. The moment that shared secret is revealed to a third party — through phishing, through a data breach, through someone looking over your shoulder — the security is gone. And unlike a physical key, you might not even know it's been copied. ## What comes after The post-password world isn't a single technology. It's a family of approaches, each suited to different contexts, all sharing a common principle: authentication should be based on something you have or something you are, not something you remember. ### Magic links The simplest passwordless approach is the magic link — an email containing a unique, time-limited URL. Click the link, and you're signed in. No password to remember, no credentials to steal through phishing, no notebook in the kitchen drawer. Magic links work because they leverage a trust relationship that already exists: your email account. If you can access your email, you can sign in. The security of your authentication is now tied to the security of your email provider, which is almost certainly better than the security of whatever password you would have chosen. The user experience is remarkably smooth. Instead of "enter your email and password," the flow is "enter your email." An email arrives within seconds. You click a button. You're in. For many users, especially those who were already using "forgot my password" as their primary login method, this feels faster and easier than what they were doing before. The tradeoff is that magic links inherit the vulnerabilities of email. If someone has compromised your email account, they can sign in to any service that uses magic links. But this is the same vulnerability that "forgot my password" has always created — if you can reset a password via email, email is already your true authentication factor. Magic links just make that reality explicit instead of pretending passwords provide additional security. ### Passkeys Passkeys are the most significant advance in consumer authentication in decades. They use the same public-key cryptography that secures the internet, but wrapped in an experience as simple as unlocking your phone. When you create a passkey for a website, your device generates a unique cryptographic key pair. The private key never leaves your device — it's stored in a secure enclave, protected by your device's biometric authentication. The public key is sent to the website. When you sign in, the website sends a challenge, your device signs it with the private key, and the website verifies the signature with the public key. At no point does a shared secret cross the network. There's nothing to steal, nothing to phish, nothing to reuse. From the user's perspective, signing in with a passkey feels like unlocking your phone. You tap "sign in," your device prompts for a fingerprint or face scan, and you're in. No typing. No remembering. The entire interaction takes about two seconds. Passkeys can also sync across devices through your platform's cloud account — Apple's iCloud Keychain, Google's Password Manager — which means you don't lose access when you get a new phone. This solves one of the historical problems with device-bound authentication: the fear that losing your device means losing access to everything. I've watched users try passkeys for the first time, and the reaction is almost always the same: a moment of confusion ("that's it?"), followed by delight. It feels like the future because it is. ### SMS and other second factors SMS one-time codes are the most widely deployed passwordless mechanism, and also the most controversial among security professionals. A six-digit code sent to your phone number provides a second factor of authentication that's easy to understand and doesn't require any special hardware or software. The controversy is about security. SMS messages can be intercepted through SIM swapping — where an attacker convinces your phone carrier to transfer your number to their device — or through network vulnerabilities. For this reason, security purists argue that SMS should never be used for authentication. But security doesn't exist in a vacuum. It exists in the context of real people making real tradeoffs. An SMS code is significantly more secure than a reused password, which is what most people are using today. If the choice is between SMS-based authentication and "password123" used across fifteen services, SMS wins by a wide margin. Perfect security that nobody uses is less secure than imperfect security that everyone uses. The right approach, I believe, is to offer SMS as an accessible option while encouraging the adoption of stronger methods like passkeys. Meet people where they are, and make it easy to upgrade. Don't let the pursuit of perfect security become the enemy of meaningful improvement. ## The migration that doesn't break trust One of the hardest parts of moving away from passwords isn't technical — it's psychological. People have muscle memory around passwords. They know the ritual: type email, type password, click sign in. Changing that ritual, even to something objectively better, creates anxiety. I've learned that the most successful passwordless migrations are gradual, not sudden. You don't wake up one morning and remove the password field. Instead, you add passwordless options alongside the existing password flow. You make the passwordless option the default, the most prominent choice on the login page. You let people try it, get comfortable with it, and eventually forget that passwords were ever involved. The worst thing you can do is force the transition. If someone has been typing a password for ten years and you suddenly tell them they can't, they feel disempowered, regardless of how much more secure the new method is. The migration needs to feel like an upgrade, not a mandate. Data supports this approach. When passwordless is offered as the default but password remains available, adoption rates typically reach 60-70% within a few months. When the password is removed entirely, support ticket volume spikes dramatically, and some users simply leave. The goal is conversion, not coercion. ## The conversion impact Here's something that surprised me the first time I saw the data: passwordless authentication measurably improves business metrics, not just security metrics. Login completion rates go up because people aren't failing password entry. Account creation rates increase because the registration flow has fewer fields. Support costs drop because "I forgot my password" — historically the number one support request at most consumer services — essentially disappears. Session duration increases because users who found login painful were visiting less frequently. These improvements aren't small. Organizations that have adopted passwordless authentication report login completion improvements of 20-30% and significant reductions in support ticket volume. For a consumer service with millions of users, those numbers translate to meaningful revenue impact. This is why I frame passwordless authentication as a user experience improvement, not just a security improvement. The security benefits are real and important — eliminating phish-able credentials removes the single largest attack vector. But the business case is compelling on its own: people use your product more when signing in isn't a chore. ## What we owe our users When I think about my mother's notebook in the kitchen drawer, I feel a mix of tenderness and frustration. Tender because she's doing the best she can with a system that wasn't designed for her. Frustrated because we — the people who build these systems — created a world where a seventy-two-year-old woman needs a handwritten cheat sheet just to access her own accounts. We can do better. The technology exists. Passkeys are supported by every major platform. Magic links require nothing more than an email account. The barriers to passwordless authentication are no longer technical — they're organizational, cultural, and a matter of prioritization. Every time we ask a user to create a password, we're asking them to do something that's hard, that they'll probably do poorly, and that creates a security vulnerability we'll have to defend against. Every time we offer a passwordless alternative, we're saying: we respect your time, we don't want to burden your memory, and we'll find a better way to keep you safe. Passwords were always the wrong answer. We just didn't have a better question until now. --- ## Josh Wu (J. Wu) I'm a developer and writer living in Seoul. For the past decade I've been building things for people — authentication systems, privacy tools, the occasional good sentence — and learning to care about getting it right. I'm a Staff Engineer at [Coupang](https://www.aboutcoupang.com/), researching customer identity and access management (CIAM) and AI-powered trust building through [zero trust architecture (ZTA)](https://csrc.nist.gov/publications/detail/sp/800-207/final). Before that I was at [Shopee](https://shopee.com/). The thread that runs through my work: earning trust through verification, not assumption. {{< tabs title="Things I believe" width="normal" style="pill" >}} {{< tab name="Trust" >}} Technology moves fast. Sometimes too fast. We are deploying [AI systems before we understand their implications](https://artificialintelligenceact.eu/), asking users to trust black boxes, and building infrastructure that prizes speed over accountability. The gap between what we build and what we can verify keeps growing. My work in [privacy by design](https://gdpr-info.eu/art-25-gdpr/) and [AI governance](https://www.nist.gov/itl/ai-risk-management-framework) — especially across [Asian markets where regulatory frameworks are evolving rapidly](https://www.pdpc.gov.sg/) — is an attempt to close that gap. To make transparency measurable, not aspirational. Every authentication flow, every privacy control, every audit trail is a mechanism that either earns trust or erodes it. I believe we can do better. {{< /tab >}} {{< tab name="Craft" >}} I am a disciple of [Stoicism](https://en.wikipedia.org/wiki/Stoicism). I hold that the purpose of a life is to build, to create, and to connect — that forging meaning through action is not merely a choice but the distinctly human one. Outside of code and prose, I am a father, a husband, a devoted cook of Chinese cuisine, and an independent stock trader. The common denominator is craft: the patient work of making something worthy with your hands and your attention. {{< /tab >}} {{< tab name="Convictions" >}} Three convictions guide me: - **Reveal the Unknown** — seek clarity where others accept fog. - **Cultivate the Wild** — nurture ideas and people that refuse neat categories. - **Realize the Potential** — close the distance between what is and what could be. {{< /tab >}} {{< /tabs >}} ## Connections If you are working on similar challenges or simply want to talk — I am most reachable by [email](mailto:mail@jwu.computer). I share work-in-progress on [GitHub](https://github.com/jwuxan), shorter thoughts on [X (@jwuxan)](https://x.com/jwuxan), and keep a professional trail on [LinkedIn](https://linkedin.com/in/zhixuan-josh-wu). ## Colophon Type is set in [Söhne](https://klim.co.nz/retail-fonts/soehne/) by [Klim Type Foundry](https://klim.co.nz/). Built with [Hugo](https://gohugo.io/), hosted on [GitHub Pages](https://pages.github.com/), and accelerated by [Cloudflare](https://www.cloudflare.com/). Design inspired by [OpenAI](https://openai.com/blog), [DESIGN.GOTHE.SE](https://design.gothe.se/cv/), and [Paco Coursey](https://paco.me). ## Syndai This site is part of [Syndai](https://syndai.com) — an independent research lab working at the intersection of AI governance and security infrastructure. We apply zero-trust verification principles to the broader challenge of making AI systems transparent, accountable, and privacy-preserving.