CLAUDE LABJP
2.1.273 — A round of connection work landed together: five opt-in headers for LLM gateways, and a notice when Claude Code stops trying to reconnect an MCP server09/29 — The date beside claude-sonnet-4-5 is 12 days out, but it is an earliest-possible estimate. The model is still Active, and public retirements get at least 60 days noticeMCP — People keep asking to reconnect a dropped server without ending the session. The disconnect is now announced, but reattaching is still something you do by handNEW — A scheduled task ran some days and not others. The cause was that only one folder had been bound to itWINDOWS — When Cowork fails on its very first task, check developer mode and the setup state before going looking for a causeHANDOFF — Before a long draft gets too heavy for one chat, decide on the three things the summary must carry into the next one2.1.273 — A round of connection work landed together: five opt-in headers for LLM gateways, and a notice when Claude Code stops trying to reconnect an MCP server09/29 — The date beside claude-sonnet-4-5 is 12 days out, but it is an earliest-possible estimate. The model is still Active, and public retirements get at least 60 days noticeMCP — People keep asking to reconnect a dropped server without ending the session. The disconnect is now announced, but reattaching is still something you do by handNEW — A scheduled task ran some days and not others. The cause was that only one folder had been bound to itWINDOWS — When Cowork fails on its very first task, check developer mode and the setup state before going looking for a causeHANDOFF — Before a long draft gets too heavy for one chat, decide on the three things the summary must carry into the next one
Articles/Cowork
Cowork/2026-08-24Intermediate

I counted the keys in my connected folder: filenames found 5 of the 18

A record of counting what lives inside a connected folder, once by filename and once by file contents. Filenames returned 24 hits of which only 5 were real, while a content scan returned 18 of which 13 left no trace in their names. Includes a scanner that never prints a secret, its real output, and the rules I adopted afterwards.

Cowork40connected folder2secret managementindie development23operations29

I was re-selecting a connected folder one morning. The dialog showed the working folder I use every day, and there was no reason to hesitate.

Then I stopped.

I could not actually say what was inside it. Four site repositories, operations documents, image assets, old backups. Add to a folder for a few years as an indie developer and this is simply what happens.

Before deciding how much to expose, I decided to count.

The short version: filenames miss about three quarters of it

Here is the result up front. The target was the same working folder I connect every day.

MethodHitsActually contained a keyMissed
Search by filename24513 files
Search by file contents1818

Searching for names containing token or secret produced 24 hits, but only 5 of them held an actual key string. The other 19 were things like article drafts about how token counting works. A check that is wrong four times out of five is a check people stop reading.

The content search produced 18 files, and 13 of those carry no hint at all in their names.

That is where "protect it by naming convention" stops working.

What was actually counted: 14,814 files out of 103,267

The first number I got was 103,267 files and 4.0 GB. That number is close to meaningless, because most of it is node_modules.

I excluded the following before scanning.

Excluded directoryWhy
node_modulesNot mine, and not somewhere keys live. Nearly 90% of the file count
.next / dist / buildBuild output. Reading the source is enough
.gitHistory needs a different hunt (keys already committed), which is a separate question
.wranglerDeploy tooling scratch space

After exclusions: 14,814 files and 2.1 GB, a little over a tenth of the original. Without that step the scan takes minutes every time and produces a result list nobody wants to read.

One note on skipping .git. A key committed to history survives deletion from the working tree, so that hunt still matters — but it answers a different question than "is it safe to expose this folder right now." Here I only looked at the working tree.

Of the 24 filename hits, 5 were real

All 19 false positives were article drafts. Their filenames contained token because they discussed token estimates and rate limits. Nothing more.

The 5 real ones broke down like this:

  • A note holding a personal access token in plain text — 1
  • Dated backups of that same note — 3
  • A note holding a webhook signing secret — 1

There is a practical cost hiding in that 19. A check that mostly cries wolf gets skimmed, then scheduled less often, then quietly dropped. I had run a name-based grep like this before and remembered it as "clean," which in hindsight probably means I stopped reading past the article filenames. Precision is not a cosmetic property of a security check; it decides whether the check survives contact with a busy week.

The line that stopped me was the three backups. You can rotate a token and still leave the old value alive under a slightly different filename. Backups quietly extend the lifetime of a key.

Stray .bak files cause trouble on the commit side too. I wrote up how a helper script's leftovers get swept in by git add -A in git add -A sweeps up your .bak backups.

The 18 content hits, and why 13 of them are invisible by name

Grouped by extension:

ExtensionCountWhat they were
.sh10Throwaway scripts written to set up billing configuration
.bak_* (dated)4Generational backups of documents and notes
.txt3Notes holding tokens and configuration values
.md1A troubleshooting log with a real key pasted into the repro steps

Shell scripts dominated. Register products and prices in bulk, create webhooks, replace descriptions across a catalog — each one ran once, did its job, and stayed. When I wrote them they were "things I run locally," and that assumption evaporates the moment the folder is handed to something else. Working as an indie developer, nobody reviews that assumption for you.

The single .md file was the one that stung. A record of a problem I had hit months earlier included the real key inside its reproduction steps. My assumption that documentation is not where keys live had become the hole in my own checking.

What those 13 invisible files have in common is simple: the person who wrote them did not think of them as secrets. They were not secret when they were named. They became secret later. Names cannot track that.

The scanner, in a form you can run today

It is written so that no key value is ever printed. The output is paths and counts only.

#!/usr/bin/env bash
# Count the exposure surface of a connected folder. Never prints key values.
set -uo pipefail
ROOT="${1:?usage: scan-exposure.sh <folder>}"
 
PRUNE=( -name node_modules -o -name .next -o -name .git
        -o -name .wrangler -o -name dist -o -name build )
NAME_PAT=( -iname "*token*" -o -iname "*secret*" -o -iname "*credential*"
           -o -iname "*.pem" -o -iname "*.key" -o -iname ".env*"
           -o -iname "id_rsa*" -o -iname "*api?key*" )
VALUE_RE='ghp_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|sk-ant-[A-Za-z0-9-]{20,}'
VALUE_RE="$VALUE_RE"'|sk_live_[A-Za-z0-9]{20,}|whsec_[A-Za-z0-9]{20,}|AIza[A-Za-z0-9_-]{30,}'
 
all=$(find "$ROOT" -type f 2>/dev/null | wc -l)
kept=$(find "$ROOT" \( "${PRUNE[@]}" \) -prune -o -type f -print 2>/dev/null | wc -l)
byname=$(find "$ROOT" \( "${PRUNE[@]}" \) -prune -o -type f \( "${NAME_PAT[@]}" \) -print 2>/dev/null | wc -l)
 
tmp=$(mktemp)
find "$ROOT" \( "${PRUNE[@]}" \) -prune -o -type f -size -2M -print0 2>/dev/null \
  | xargs -0 grep -lIE "$VALUE_RE" 2>/dev/null > "$tmp"
 
printf 'files(all)        %s\n' "$all"
printf 'files(scanned)    %s\n' "$kept"
printf 'hits(by filename) %s\n' "$byname"
printf 'hits(by content)  %s\n' "$(wc -l < "$tmp")"
printf -- '--- by extension ---\n'
sed 's|.*/||; s|^[^.]*$|(no ext)|; s|.*\.|.|' "$tmp" | sort | uniq -c | sort -rn
rm -f "$tmp"

Real output from my machine:

files(all)        103267
files(scanned)    14814
hits(by filename) 24
hits(by content)  18
--- by extension ---
     10 .sh
      3 .txt
      1 .md
      1 .bak_20260822
      1 .bak_20260602b
      1 .bak_2026-07-28
      1 .bak_20260602
 
real	0m32.147s

Three notes on why it is written this way.

grep -l returns filenames only and never the matching line, so the scan result itself does not become a new leak path. -I skips binaries, which matters a lot in a folder full of image assets. -size -2M keeps large logs and build artifacts out of the comparison; a key note is never two megabytes.

The regex list only covers keys with fixed prefixes. Values without a recognizable prefix will not be caught this way. I use it knowing that limit, as a tool that reliably catches what it can catch.

Thirty-two seconds is light enough to run once a month without thinking about it.

What I changed afterwards

Three things went into my own routine:

  1. Key notes move outside the connected folder. When the unit of sharing is a whole folder, "no secrets live in this folder" has to be decided first, or the boundary shifts every time.
  2. No generational backups of key notes. Those three backups were keeping replaced values alive. One copy now, updated in place.
  3. Scan by content, not by name, once a month. A check with a 79% false positive rate was not worth continuing.

Order matters in the cleanup, and I got it wrong on the first pass. My instinct was to delete the files immediately, which felt satisfying and accomplished very little: the values had already been sitting in a shared folder, so the exposure had happened regardless of whether the file still existed. Rotating the credential is what actually ends it. Deleting first also destroys the evidence of which key was where, which is exactly what you need in order to rotate the right things. So the sequence I use now is rotate, then relocate, then delete — and only then re-run the scan to confirm the count went to zero.

Counting is only the entrance. The next step is making the files unreadable in the first place, and that belongs to configuration. The sandbox.credentials settings that stop credential files from being read at the OS level, along with how to verify they are working, are written up with the implementation in The sandbox can run your code without reading your credentials. Reading it after you have your own counts makes it much clearer which setting removes which files from the list.

The useful part of today was not the number 18. It was that 13 of them were invisible by name — the exact size of the gap between how well I thought I was managing this and how well I actually was.

If you have a connected folder, run the script once. A count of zero is a real basis for confidence. A count above zero is something you can act on today, while it is still cheap.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Cowork2026-09-16
The scheduled task that only ran on days my desk was awake
Cowork scheduled tasks run remotely by default, but the moment one needs a local file or app, it runs only on your machine. Here is how the last field in the setup dialog decides that, and the three questions I now answer before creating a task.
Cowork2026-09-05
I verify what my Cowork memory claims instead of trusting its timestamp
Persistent memory keeps asserting whatever was true the day you wrote it. After an unattended job quietly read an empty folder for months, I stopped judging memory by its modification date and started attaching a verification step to every claim that can rot.
Cowork2026-08-29
The lock I left in a shared folder shut out every run after the first
A cloud-synced connected folder allows create, append, and rename, but refuses delete. Putting a single-instance lock there quietly disabled every unattended run after the first. Measurements for three lock styles, plus an implementation that picks a safe location.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links