# Reach Inventory: five rules and a five-check script (free sample)

Version 1.0, 2026-10-29. By PaperChaseWebb. For owners whose agents hold connectors, in Grok Bot, Claude Code, Codex, Hermes or a mix, and who want one list of what those agents can reach. One Markdown file and one Python 3 script, standard library only. The script was tested on its worked example (5 findings, then 0) and on a missing file (exit 2). It was not run inside any agent harness or against real connected accounts.

Connectors pile up. One gets added for an afternoon's job and stays for a year. An automation bridge is connected once and from then on reaches every app behind it. Nobody remembers which grant does what, so nothing gets removed. These five rules cover the gaps that cost the most, and the script checks them in one small file.

## Install in one line

Save this page as FREE-SAMPLE.md, then:

```
python3 -c "import re;t=open('FREE-SAMPLE.md').read();open('reach_mini.py','w').write(re.search(r'~~~python\n(.*?)~~~',t,re.S).group(1))"
python3 reach_mini.py reach-lite.json
```

## Five rules

1. **The list has a human owner.** A person decides what stays and what goes. Not "TBD", and not the agent that holds the access.
2. **Every connector serves a named job.** If nobody can say what job it does, it goes on the removal list.
3. **Leftover means removed.** Access with no current job is switched off, not kept "just in case".
4. **Write access has an approver.** Anything that can send, post, book or change records names the person who says yes first.
5. **Unused access is reviewed.** Anything not used in 30 days before the audit gets a second look.

## The five checks

| Id | Check |
|---|---|
| M1 | the list names a human owner |
| M2 | every active connector names the job it serves |
| M3 | nothing marked leftover is still active |
| M4 | every active connector with write access names an approver |
| M5 | every active connector was used in the 30 days before the audit date |

Exit 0 clean, 1 findings, 2 missing or unreadable file. The script only reads.

~~~python
#!/usr/bin/env python3
"""reach_mini.py: five read-only checks on a one-file reach list (reach-lite.json).
Usage: python3 reach_mini.py reach-lite.json
Exit 0 clean, 1 findings, 2 missing or unreadable file. Standard library only. Writes nothing."""
import datetime as dt, json, sys

PLACEHOLDERS = {"", "tbd", "todo", "owner", "someone", "agent", "ai", "assistant", "team", "default", "unknown"}


def day(s):
    try:
        return dt.date.fromisoformat(str(s or "").strip())
    except Exception:
        return None


def main(a):
    if len(a) < 2:
        print(__doc__); return 2
    try:
        b = json.load(open(a[1]))
    except Exception as e:
        print(f"cannot read {a[1]}: {e}", file=sys.stderr); return 2
    F = []
    if str(b.get("owner", "")).strip().lower() in PLACEHOLDERS:
        F.append("M1 the reach list has no human owner")
    audited = day(b.get("audited_on")) or dt.date.today()
    for c in b.get("connectors", []):
        name, live = c.get("name", "?"), c.get("status") == "active"
        if live and str(c.get("job", "")).strip().lower() in PLACEHOLDERS:
            F.append(f"M2 {name} is active with no job named; access without a job goes on the removal list")
        if live and c.get("need") == "leftover":
            F.append(f"M3 {name} is leftover and still active")
        if live and c.get("access") in ("write", "admin") and c.get("approver", "").strip().lower() in PLACEHOLDERS:
            F.append(f"M4 {name} can write and nobody is named to approve what it writes")
        lu = day(c.get("last_used"))
        if live and (not lu or (audited - lu).days > 30):
            F.append(f"M5 {name} has not been used in the 30 days before the audit")
    F.sort(key=lambda x: x[:2])
    for f in F:
        print(f)
    print(f"5 checks, {len(F)} findings")
    return 1 if F else 0


if __name__ == "__main__":
    sys.exit(main(sys.argv))
~~~

## Worked example

A made-up dog grooming shop with four agents. Nobody owns the list, the calendar connector has no job, a write bridge has no approver, and an old survey connector is marked leftover but still on. Before (reach-lite.json):

```json
{
 "business": "Brightwater Dog Grooming (made up)",
 "owner": "",
 "audited_on": "2026-10-29",
 "connectors": [
  {"name": "Gmail connector", "agent": "Claude Code", "job": "reply drafts", "need": "necessary", "access": "read", "approver": "", "last_used": "2026-10-28", "status": "active"},
  {"name": "Google Calendar connector", "agent": "Grok Bot", "job": "", "need": "convenient", "access": "read", "approver": "", "last_used": "2026-10-27", "status": "active"},
  {"name": "Zapier MCP", "agent": "Codex", "job": "booking texts", "need": "necessary", "access": "write", "approver": "", "last_used": "2026-10-26", "status": "active"},
  {"name": "Old survey connector", "agent": "Hermes", "job": "feedback survey", "need": "leftover", "access": "read", "approver": "", "last_used": "2026-07-02", "status": "active"}
 ]
}
```

Output:

```
M1 the reach list has no human owner
M2 Google Calendar connector is active with no job named; access without a job goes on the removal list
M3 Old survey connector is leftover and still active
M4 Zapier MCP can write and nobody is named to approve what it writes
M5 Old survey connector has not been used in the 30 days before the audit
5 checks, 5 findings
```

The fixes: Ines is named as the owner, the calendar connector gets its job (the morning schedule), Ines is named as approver on the Zapier bridge, and the old survey connector is removed in its app settings and marked removed. After:

```
5 checks, 0 findings
```

## One prompt to try

Paste into your agent: "List every connector, MCP server and API key you can use, names only and never values. Write them to reach-lite.json with the job each serves, necessary, convenient or leftover, read or write, and the date each was last used where you can find it. Do not connect, remove or change anything."

## The full pack

The paid Reach Inventory pack (pp-reach-inventory, $19) grows this into a full audit: twenty-two columns per piece of access including scope granted against scope needed, whose login it runs as, where the credential is kept and when it expires; fifteen checks; bridges listed app by app; a written reason for any agent that reads outside content, sees private data and can send data out; removal with proof; and changes that wait for the owner's yes, tied to the inventory hash and typed at their own terminal. If you would rather have it done with you, book a call from the home page at paperchasewebb.ai.

Made-up example; the business, people and data are invented.

PaperChaseWebb 💸
