How the Owner-Liveness-Sweep Detects Plugin Source Repo Ownership Changes
The owner-liveness-sweep detects ownership changes by comparing each plugin owner's current GitHub account ID against a stored baseline, flagging any mismatch as an identity_changed finding.
The anthropics/claude-plugins-community repository runs a daily security sweep to ensure that external plugin source repositories haven't changed hands unexpectedly. This sweep protects the marketplace from name-squatting attacks, where a GitHub username is abandoned and later claimed by a different account. The owner-liveness-sweep action automates this verification using GitHub's stable account identifiers and a version-controlled baseline.
How the Ownership Detection Works
The sweep follows a five-step process to identify any ownership changes across all marketplace entries.
Step 1: Extract Owners from Marketplace Entries
The script parses .claude-plugin/marketplace.json and extracts GitHub URLs from every plugin entry. For each URL, it isolates the owner login using a regex pattern in .github/actions/owner-liveness-sweep/scripts/sweep.sh at lines 60-102.
# From sweep.sh L60-L102: extracts unique GitHub owners from the marketplace
echo "$(jq -c '
[.plugins[] | select(.source?.repository? | strings | test("^https://github.com/"; "i"))
| .source.repository
| capture("^https://github\\.com/(?<owner>[^/]+)"; "i").owner
| ascii_downcase]
| unique
' "$MARKETPLACE_PATH")"
Step 2: Resolve Owners via GraphQL
The script queries GitHub's GraphQL API to resolve each login to its current databaseId (the immutable account identifier). The query template appears at lines 22-30 of sweep.sh:
{#query}
repositoryOwner(login:"$login") {
__typename
...on User { id databaseId }
...on Organization { id databaseId }
}
This batched query runs for all discovered owners, returning both whether the login is still active (live == true) and the numeric databaseId.
Step 3: Load the Stored Baseline
The baseline file .github/owner-baseline.json maps each owner login (lower-cased) to the databaseId recorded when that owner was first added to the marketplace. This JSON file serves as the source of truth for ownership verification.
Step 4: Compare Live IDs Against Baseline
The core detection logic at lines 182-185 of sweep.sh identifies ownership changes:
# From sweep.sh L182-185: identity change detection
if [[ "$live_id" -ne "$recorded_id" ]]; then
finding_type="identity_changed"
finding_payload=$(jq -n --arg o "$owner" --argjson rid "$recorded_id" --argjson lid "$live_id" '{owner:$o, recorded_id:$rid, live_id:$lid}')
fi
A mismatch between live_id and recorded_id indicates the login now belongs to a different GitHub account, even if the username appears identical.
Step 5: Fail the Run on Identity Changes
Unless FAIL_ON_IDENTITY_CHANGE is set to false, the sweep exits with an error when any identity_changed finding exists (lines 84-87). This forces human review before automated actions like version bumps proceed.
# From sweep.sh L84-87: fail-on-change handling
if [[ "${FAIL_ON_IDENTITY_CHANGE:-true}" == "true" ]] && [[ "$(jq '.identity_changed | length' findings.json)" -gt 0 ]]; then
echo "Error: Identity changes detected. Review findings.json" >&2
exit 1
fi
Running the Owner-Liveness-Sweep
The sweep supports two operational modes controlled by the MODE environment variable.
Report Mode (Default)
Use this mode in CI to detect and block on ownership changes:
MARKETPLACE_PATH=.claude-plugin/marketplace.json \
BASELINE_PATH=.github/owner-baseline.json \
MODE=report \
bash .github/actions/owner-liveness-sweep/scripts/sweep.sh
Refresh Mode
Use this mode after manually verifying ownership changes to update the baseline:
MARKETPLACE_PATH=.claude-plugin/marketplace.json \
BASELINE_PATH=.github/owner-baseline.json \
MODE=refresh \
bash .github/actions/owner-liveness-sweep/scripts/sweep.sh
Understanding the Findings Output
Both modes generate a findings.json file with structured results. An identity_changed entry appears as follows:
{
"identity_changed": [
{
"owner": "exampleOwner",
"recorded_id": 1234567,
"live_id": 7654321,
"entries": ["plugin-A", "plugin-B"]
}
],
"unresolved": [],
"identity_intact": []
}
recorded_id: The original GitHub account ID from the baselinelive_id: The current GitHub account ID for that loginentries: Plugin entries that reference this owner
Key Components of the Detection System
| File | Purpose |
|---|---|
.github/workflows/owner-liveness-sweep.yml |
Daily GitHub Actions workflow invoking the sweep |
.github/actions/owner-liveness-sweep/scripts/sweep.sh |
Core detection logic, GraphQL queries, and comparison |
.github/actions/owner-liveness-sweep/README.md |
Documentation of detection classes and baseline handling |
.github/owner-baseline.json |
Version-controlled mapping of owner logins to account IDs |
Summary
- The owner-liveness-sweep protects against account takeovers by tracking immutable GitHub
databaseIdvalues, not just usernames. - Identity changes are detected when a resolved
live_iddiffers from therecorded_idin.github/owner-baseline.json. - The sweep fails by default on identity changes, requiring human review before any automated marketplace updates.
- Use report mode for CI detection and refresh mode to update the baseline after verified ownership changes.
- The entire system is implemented in the
anthropics/claude-plugins-communityrepository as a Bash script with GitHub Actions integration.
Frequently Asked Questions
What triggers an identity_changed finding?
An identity_changed finding triggers when a GitHub login that exists in the baseline resolves to a different databaseId than previously recorded. This occurs when the original account deleted or renamed their account, and a new account claimed the same username.
Why use databaseId instead of usernames for ownership tracking?
GitHub usernames are mutable—users can change them or delete their accounts. The databaseId is an immutable integer assigned at account creation. Comparing these stable identifiers prevents false negatives when a username gets recycled.
How do I update the baseline after a legitimate ownership change?
Run the sweep in refresh mode after manually verifying the new owner is legitimate: set MODE=refresh and execute sweep.sh. This updates .github/owner-baseline.json with current databaseId values for all resolved owners.
Can I disable the failure on identity changes?
Yes—set the environment variable FAIL_ON_IDENTITY_CHANGE=false before running the sweep. This allows the workflow to complete and report findings without blocking, useful for testing or transitional review workflows.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →