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 baseline
  • live_id: The current GitHub account ID for that login
  • entries: 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 databaseId values, not just usernames.
  • Identity changes are detected when a resolved live_id differs from the recorded_id in .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-community repository 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →