惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Full Disclosure
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
The GitHub Blog
The GitHub Blog
WordPress大学
WordPress大学
博客园 - 三生石上(FineUI控件)
D
Docker
K
Kaspersky official blog
Latest news
Latest news
P
Privacy International News Feed
雷峰网
雷峰网
S
Security Affairs
博客园 - 司徒正美
博客园 - Franky
C
CERT Recently Published Vulnerability Notes
Cisco Talos Blog
Cisco Talos Blog
AWS News Blog
AWS News Blog
C
Check Point Blog
T
Tailwind CSS Blog
T
Tenable Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Threat Research - Cisco Blogs
The Last Watchdog
The Last Watchdog
Google Online Security Blog
Google Online Security Blog
L
Lohrmann on Cybersecurity
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
B
Blog
The Hacker News
The Hacker News
V
V2EX
L
LINUX DO - 最新话题
云风的 BLOG
云风的 BLOG
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
W
WeLiveSecurity
Know Your Adversary
Know Your Adversary
P
Proofpoint News Feed
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Microsoft Security Blog
Microsoft Security Blog
S
Securelist
V2EX - 技术
V2EX - 技术
博客园 - 叶小钗
The Cloudflare Blog
小众软件
小众软件
Recent Announcements
Recent Announcements
Microsoft Azure Blog
Microsoft Azure Blog
L
LangChain Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Hacker News: Ask HN
Hacker News: Ask HN
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The Register - Security
The Register - Security

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
"Persisting your real Chrome login across Playwright restarts on macOS"
Takayuki Kaw · 2026-05-10 · via DEV Community

Every macOS reboot, the same ritual. Open the Playwright-controlled Chrome window, see seven publishing tabs all logged out, and spend the next ten minutes typing passwords and tapping the Google account picker. Zenn, dev.to, note, Substack, X, LinkedIn, the Google Search Console dashboard. All gone, all needing the same Google SSO dance through my corevice.com workspace account.

I run a one-person GTM operation for Codens and the publishing pipeline is entirely Playwright-driven. npx @playwright/cli@latest opens a real Chrome with a persistent profile, and a stack of small scripts paste titles and bodies into each editor. It works beautifully until the host reboots and the user-data-dir at /tmp/chrome-pw-corevice evaporates with the rest of /tmp.

I finally sat down and fixed it. The result is a thirty-line shell script that clones my daily-driver Chrome profile into the Playwright tmpdir on every launch, with two non-obvious tricks that make the cookies actually decrypt. This post is about those two tricks.

Why the obvious copy doesn't work

The first thing anyone tries is the obvious thing.

cp -r ~/Library/Application\ Support/Google/Chrome/Default \
      /tmp/chrome-pw-corevice/Default

Enter fullscreen mode Exit fullscreen mode

Run it, fire up Playwright, and Chrome opens looking like it has my profile. History is there. Bookmarks are there. Extensions are there. But every site is logged out, and the cookie jar in DevTools is either empty or full of cookies that don't authenticate anything.

The reason is that Playwright launches Chrome with two flags I didn't know about until I started digging:

--use-mock-keychain
--password-store=basic

Enter fullscreen mode Exit fullscreen mode

Those flags tell Chrome to bypass the macOS Keychain entirely and use a hardcoded mock encryption key for cookies and the password store. From Playwright's point of view this is the right default. CI runners don't have a real keychain. Headless containers don't have a real keychain. The mock makes Chrome boot reliably in places where Keychain Access doesn't exist.

But for me, this is exactly wrong. The cookies my daily-driver Chrome wrote to disk were encrypted with the real keychain key, the one Chrome stored under "Chrome Safe Storage" in my login keychain on first install. The cookies that just got copied over are still encrypted with that real key. Playwright's Chrome boots with the mock key, tries to decrypt them, gets garbage, and silently treats every cookie as invalid.

I tried storageState first, which is the documented Playwright path for this. Export cookies and localStorage from one context, inject into another. It works for some sites and dies for others. Substack stalled at the Google SSO redirect and never finished the auth handshake. Note's editor wanted a CSRF token tied to a session cookie that storageState had captured but which the server no longer accepted, presumably because the session was bound to the original UA fingerprint. After the third site failed in a different way I gave up on storageState and went back to cloning the whole profile.

So: two real fixes are needed. Make Playwright's Chrome speak the same encryption language as my daily Chrome, and copy the cookie database in a way that doesn't corrupt it.

Fix one, patch the keychain flag

Playwright's CLI bundles its Chrome launch arguments inside playwright-core/lib/coreBundle.js. When you run npx @playwright/cli@latest, npm caches that file under ~/.npm/_npx/<hash>/node_modules/playwright-core/lib/coreBundle.js. The file is huge and minified, but the two strings I care about appear verbatim:

"--use-mock-keychain",
"--password-store=basic"

Enter fullscreen mode Exit fullscreen mode

A sed rewrite is enough. Swap the first to --use-real-keychain and the second to --password-store=keychain. Chrome on macOS recognizes both, and once they're in place the launched Chrome reads its encryption key from the same login keychain entry as my daily-driver Chrome. The cookies decrypt. SSO holds.

The patch wants to be idempotent because npx happily re-extracts the package if it gets purged from the cache, and I don't want to re-edit the file by hand each time. So the script does three things. It locates the bundle with find. It checks whether the bundle still contains --use-mock-keychain, which means it hasn't been patched yet. If so, it makes a .bak copy on first patch and runs sed -i '' in place.

The .bak is the escape hatch. If a future Playwright update changes those flags or relies on the mock keychain elsewhere and my patch breaks something, I can mv coreBundle.js.bak coreBundle.js and be back to stock in one command.

The first time you launch the patched Chrome, macOS will pop a Keychain Access dialog asking you to allow access to "Chrome Safe Storage." Click Always Allow. After that, no prompts.

Fix two, SQLite backup for the cookie file

With the keychain flag patched, the next failure mode is more subtle. Sometimes the cookies decrypt, sometimes they don't, and when they don't, the SQLite file looks corrupt. Chrome refuses to read it and silently starts a fresh empty cookie jar.

Chrome's Cookies file is a SQLite database. My daily-driver Chrome is almost always running, which means it's holding write locks on that database, and depending on timing it may have a partial write in progress when cp reads the file. The result is a torn copy: the bytes are physically there, but the SQLite page checksums don't match the WAL log, and SQLite refuses to open it.

The right tool for snapshotting a live SQLite database is the .backup command:

sqlite3 "${SOURCE_PROFILE}/Default/Cookies" \
  ".backup ${PW_PROFILE_DIR}/Default/Cookies"

Enter fullscreen mode Exit fullscreen mode

This isn't just a smarter copy. It uses SQLite's online backup API, which acquires a read lock, copies pages in a way that's transactionally consistent with the source database's current state, and produces a target file that opens cleanly. You can run it while Chrome is actively writing to the source. The output is always a valid database.

The script removes the stale Cookies and Cookies-journal files first, then runs .backup on every launch. That way the cookie jar is always fresh, even if I haven't rebooted but I have used my daily Chrome to log into a new site since the last Playwright session.

The script

The whole thing is at runbooks/launch/playwright-launch.sh in my GTM repo. Roughly thirty lines if you don't count comments.

#!/usr/bin/env bash
# Launch playwright-cli against the corevice.com Chrome profile copy.
# Idempotent: if profile copy missing, re-creates it; if patch missing, re-applies.
# Usage: ./playwright-launch.sh open <url>
#        ./playwright-launch.sh <command> [args...]

set -euo pipefail

PW_CACHE_BASE="${HOME}/.npm/_npx"
PW_PROFILE_DIR="/tmp/chrome-pw-corevice"
SOURCE_PROFILE="${HOME}/Library/Application Support/Google/Chrome"

ensure_profile_copy() {
  if [ ! -d "${PW_PROFILE_DIR}/Default" ] || [ ! -f "${PW_PROFILE_DIR}/Default/Cookies" ]; then
    echo "[setup] copying profile..."
    mkdir -p "${PW_PROFILE_DIR}/Default"
    rsync -a \
      --exclude='Cache' \
      --exclude='Code Cache' \
      --exclude='GPUCache' \
      --exclude='Service Worker' \
      --exclude='ShaderCache' \
      --exclude='GraphiteDawnCache' \
      --exclude='component_crx_cache' \
      --exclude='extensions_crx_cache' \
      --exclude='Sessions' \
      --exclude='File System' \
      --exclude='blob_storage' \
      --exclude='Cookies' \
      --exclude='Cookies-journal' \
      "${SOURCE_PROFILE}/Default/" "${PW_PROFILE_DIR}/Default/"
    cp "${SOURCE_PROFILE}/Local State" "${PW_PROFILE_DIR}/" 2>/dev/null || true
    cp "${SOURCE_PROFILE}/First Run" "${PW_PROFILE_DIR}/" 2>/dev/null || true
  fi

  # Always refresh cookies via SQLite .backup (safe with Chrome running)
  rm -f "${PW_PROFILE_DIR}/Default/Cookies" "${PW_PROFILE_DIR}/Default/Cookies-journal"
  sqlite3 "${SOURCE_PROFILE}/Default/Cookies" \
    ".backup ${PW_PROFILE_DIR}/Default/Cookies" 2>/dev/null
}

ensure_patch() {
  local cb
  cb=$(find "${PW_CACHE_BASE}" -path '*playwright-core/lib/coreBundle.js' 2>/dev/null | head -1)
  if [ -z "$cb" ]; then
    echo "[setup] @playwright/cli not yet installed; npx -y will install it"
    return
  fi
  if grep -q -- '--use-mock-keychain' "$cb"; then
    echo "[setup] patching playwright to use real keychain..."
    [ ! -f "${cb}.bak" ] && cp "$cb" "${cb}.bak"
    sed -i '' \
      -e 's|"--password-store=basic"|"--password-store=keychain"|' \
      -e 's|"--use-mock-keychain",|"--use-real-keychain",|' \
      "$cb"
  fi
}

ensure_profile_copy
ensure_patch

export PATH="${HOME}/.asdf/shims:${PATH}"

if [ "${1:-}" = "open" ]; then
  shift
  exec npx -y @playwright/cli@latest open --headed "$@" \
    --browser chrome \
    --profile "${PW_PROFILE_DIR}"
fi

exec npx -y @playwright/cli@latest \
  "$@" \
  --browser chrome \
  --profile "${PW_PROFILE_DIR}"

Enter fullscreen mode Exit fullscreen mode

A few notes on the rsync exclude list. All the cache directories are excluded because they're large, regenerable, and sometimes hold OS-specific binary blobs that Chrome will rebuild on first launch. Sessions is excluded so Playwright's Chrome doesn't try to restore tabs from my daily browsing. File System and blob_storage are excluded for size. Cookies and Cookies-journal are excluded specifically because we handle them via .backup immediately after the rsync, and we want that to be the authoritative copy.

Local State and First Run are copied separately. Local State is where Chrome stores the encrypted master key reference and a few profile-level settings. First Run is a sentinel file that suppresses the first-run wizard.

The patched diff itself is two lines:

- "--use-mock-keychain",
+ "--use-real-keychain",
- "--password-store=basic"
+ "--password-store=keychain"

Enter fullscreen mode Exit fullscreen mode

That's the whole keychain fix. Two strings.

A side issue, headless mode breaks the clipboard

The script forces --headed for the open subcommand, and there's a story behind that. My publish scripts work by pbcopy-ing the title and body into the clipboard, focusing the editor field via Playwright, and then sending Cmd+V. CodeMirror, Substack's editor, dev.to's editor — they all behave better with a real paste than with type() calls that fire individual keypress events. Markdown formatting survives. Code blocks stay intact. Smart-quote autocorrect doesn't fire.

But headless Chromium doesn't have a system clipboard. navigator.clipboard.readText() returns empty, the paste handler sees no data, and the form silently stays empty. I lost an hour to that one before realizing the open command was defaulting to headless mode in the version of @playwright/cli I was on. Forcing --headed makes the daemon run as a real Chrome window with full clipboard access, which is what I want anyway because I sometimes want to glance at the publish flow while it's running.

The non-open commands pass through unchanged, so anything else that wants headless behavior still gets it.

What it's worth

Five to ten minutes of manual relogins per reboot, multiplied by however often macOS decides to update overnight. Across a year that's hours I get back, and more importantly the publish scripts now run unattended. I push a draft, the script opens the right tab, pastes the right content, and I review the rendered preview before clicking publish.

If you're running a similar setup, the script is generic. Change SOURCE_PROFILE if you use a non-Default Chrome profile, change PW_PROFILE_DIR if you don't trust /tmp to survive your reboot policy, and the rest should work.

This is the kind of small infrastructure work that makes solo operations possible. We build a lot of these at Codens, where the day job is wiring AI agents into the same kind of publishing and dev pipelines.