Claude
Skills
Sign in
Back

diff-profile-archives

Included with Lifetime
$97 forever

Compare two Kernel profile archives to investigate behavioral differences. Use when an issue (e.g. login failure, captcha, broken automation, vendor mismatch) reproduces on one profile but not another, or when a "good" vs "bad" profile needs to be diffed. Takes two profile IDs/names plus an issue description, downloads both archives via the Kernel CLI, and surfaces differences in cookies, storage, preferences, extensions, and login state that could explain the issue.

General

What this skill does


# Diff Kernel Profile Archives

Compare the actual contents of two Kernel browser profiles to identify state differences that could explain a reported issue. The skill downloads both profile archives via the Kernel CLI, decompresses them, and produces a structured diff focused on the surfaces most likely to drive divergent behavior: cookies, local/session storage, preferences, extension state, login data, and history.

## Inputs

The user must provide:

1. **Profile A** — the "baseline" or "working" profile (ID or name)
2. **Profile B** — the "subject" or "broken" profile (ID or name)
3. **Issue description** — what's happening on B that isn't happening on A (e.g. "login flow on chase.com fails on profile B but works on A", "datadome challenge always fires on B but never on A", "saved credentials missing")

If any of these are missing, prompt for them before proceeding. The issue description is critical — it determines which surfaces of the profile to weight heavily in the diff.

## Prerequisites

- Kernel CLI installed and authenticated (`kernel auth status`)
- `KERNEL_API_KEY` set, or the user is logged in via `kernel auth login`
- `zstd`, `tar`, `sqlite3`, and `jq` available (install via `apt-get install -y zstd sqlite3 jq` if needed)
- ~500MB free disk space per profile (profiles can be large)

## Workflow

### Step 1: Set up workspace

```bash
WORKDIR=$(mktemp -d -t profile-diff-XXXXXX)
cd "$WORKDIR"
mkdir -p a b diff
echo "Workspace: $WORKDIR"
```

### Step 2: Download both archives

```bash
# Replace <A> and <B> with the profile IDs or names from the user
kernel profiles download <A> --pretty --to a/profile.zip
kernel profiles download <B> --pretty --to b/profile.zip
```

`--pretty` pretty-prints JSON files (e.g. `Preferences`) so they diff cleanly line-by-line. Always pass it.

### Step 3: Extract

The archive is **Zstandard-compressed TAR despite the `.zip` extension**. Standard unzip will fail.

```bash
for side in a b; do
  zstd -d "$side/profile.zip" -o "$side/profile.tar"
  tar -xf "$side/profile.tar" -C "$side"
  rm "$side/profile.tar"
done
```

After extraction each side typically contains a Chrome user-data-dir layout (e.g. `Default/` with `Cookies`, `Local Storage/`, `Preferences`, etc.). If the layout differs, run `find a -maxdepth 3 -type d` to orient yourself before continuing.

### Step 4: Triage by issue description

Pick the surfaces to compare based on what the user described. Don't compare everything — full Chrome profiles contain hundreds of files, most irrelevant. Use this routing:

| Issue keywords | Compare these first |
|---|---|
| login, signed in, session, "logged out" | Cookies, **Session Storage**, Login Data, Local Storage |
| SPA / single-page app login (any URL with `#/...` route) | **Session Storage first**, then Local Storage, then Cookies — SPAs commonly persist auth state in `sessionStorage` rather than cookies |
| captcha, datadome, cloudflare, akamai, challenge, blocked | Cookies (vendor-specific names), Local Storage, fingerprinting indicators in Preferences |
| extension, plugin, content script | `Extensions/`, `Local Extension Settings/`, `Extension State`, `Secure Preferences` |
| autofill, saved password, credential | Login Data, Login Data For Account, Web Data |
| settings, language, timezone, UA, viewport | Preferences, Secure Preferences |
| history, cache, "remembers" | History, Top Sites, Visited Links |
| storage, indexeddb, quota | Local Storage, Session Storage, IndexedDB, Service Worker |
| profile saved before post-login bootstrap finished (save-time race) | Session Storage (sentinel keys absent), Local Storage analytics events as a bootstrap-timing tracer (e.g. Mixpanel `User Login`, Segment `track`) |

When in doubt, start with **Cookies + Session Storage + Local Storage + Preferences** — they explain the majority of "works on A, fails on B" issues. For SPA-style sites that route via URL hash (`/#/...`), put **Session Storage first**: the in-tab auth often lives there, and cookies alone won't tell the story.

### Step 5: Run focused diffs

#### Cookies (SQLite)

> Modern Chrome (post-v80) encrypts cookie bodies into `encrypted_value` and leaves `value` empty. Always select `length(encrypted_value)` — `length(value)` will return `0` for every row and mask real differences.

```bash
for side in a b; do
  # Cookies live under Default/Cookies (or Default/Network/Cookies on newer Chrome)
  COOKIE_DB=$(find "$side" -name 'Cookies' -not -name '*-journal' | head -1)
  if [ -z "$COOKIE_DB" ]; then
    echo "(no Cookies DB)" > "diff/cookies-$side.txt"
    continue
  fi
  sqlite3 "$COOKIE_DB" \
    "SELECT host_key, name, length(encrypted_value), is_secure, is_httponly, samesite, expires_utc, source_scheme
     FROM cookies ORDER BY host_key, name;" \
    > "diff/cookies-$side.txt" 2>/dev/null || echo "(cookies query failed)" > "diff/cookies-$side.txt"
done
diff -u diff/cookies-a.txt diff/cookies-b.txt > diff/cookies.diff || true
```

For issue-relevant hosts only:

```bash
HOST=chase.com  # adapt to the issue
for side in a b; do
  sqlite3 "$(find $side -name Cookies -not -name '*-journal' | head -1)" \
    "SELECT host_key, name, length(encrypted_value), expires_utc, is_secure, is_httponly
     FROM cookies WHERE host_key LIKE '%$HOST%' ORDER BY host_key, name;" \
    > "diff/cookies-$HOST-$side.txt"
done
diff -u "diff/cookies-$HOST-a.txt" "diff/cookies-$HOST-b.txt" || true
```

Common bot-detection cookie names to flag if present in one and not the other: `_abck`, `bm_sz`, `bm_sv` (Akamai); `__cf_bm`, `cf_clearance` (Cloudflare); `datadome` (DataDome); `_px*`, `pxvid` (HUMAN/PerimeterX); `incap_ses_*`, `visid_incap_*`, `reese84` (Imperva).

#### Preferences (JSON)

```bash
for side in a b; do
  PREF=$(find "$side" -name 'Preferences' -not -path '*/Extension*' | head -1)
  if [ -z "$PREF" ]; then
    echo "(no Preferences)" > "diff/preferences-$side.json"
    continue
  fi
  # Already pretty-printed thanks to --pretty
  cp "$PREF" "diff/preferences-$side.json"
done
diff -u diff/preferences-a.json diff/preferences-b.json > diff/preferences.diff || true
```

Highlight any differences in: `intl.*` (locale, accept_languages), `profile.default_content_setting_values`, `webrtc.*`, `extensions.settings` (set of installed extensions), `dns_over_https.*`, `download.*`, `safebrowsing.*`.

#### Local Storage (LevelDB)

`ls -la` on `Local Storage/leveldb/` only tells you whether the dir exists. To actually inspect keys and values without writing a LevelDB reader, run `strings` on the `*.log` and `*.ldb` files — Chrome stores keys and most string values in plaintext inside leveldb, so `strings | grep` gets you 90% of the way for free.

```bash
for side in a b; do
  LS_DIR=$(find "$side" -type d -name 'Local Storage' -not -path '*/Extension*' | head -1)
  if [ -z "$LS_DIR" ]; then
    echo "(no Local Storage)" > "diff/localstorage-files-$side.txt"
    echo "(no Local Storage)" > "diff/localstorage-origins-$side.txt"
    echo "(no Local Storage)" > "diff/localstorage-signals-$side.txt"
    continue
  fi
  # File inventory
  ls -la "$LS_DIR/leveldb" > "diff/localstorage-files-$side.txt" 2>&1
  # All origins that have any data
  strings "$LS_DIR"/leveldb/*.{log,ldb} 2>/dev/null \
    | grep -oE 'META:https?://[^[:space:]]+|^_https?://[^[:space:]^]+' \
    | sort -u > "diff/localstorage-origins-$side.txt"
  # Key inventory per matching origin (adapt grep to the issue host)
  strings "$LS_DIR"/leveldb/*.{log,ldb} 2>/dev/null \
    | grep -aE '"event"|"User Login"|"track"|sessionGUID|authCookie|expirationDateTime|access_token|id_token|refresh_token' \
    | head -100 > "diff/localstorage-signals-$side.txt"
done
diff -u diff/localstorage-files-a.txt diff/localstorage-files-b.txt || true
diff -u diff/localstorage-origins-a.txt diff/localstorage-origins-b.txt || true
diff -u diff/localstorage-signals-a.txt diff/localstorage-signals-b.txt || true
```

**Analytics events as a bootstrap-timing tracer.** For SPA logins it's

Related in General