Go to file
claude-ceo-assistant b2ed330475
All checks were successful
CI / Shellcheck + wrapper tests (pull_request) Successful in 24s
CI / Go build + test + vet (pull_request) Successful in 1m10s
fix(post-suspension): vanity import path go.moleculesai.app/plugin/gh-identity (closes molecule-ai/internal#71 phase 2)
Migrates go.mod + the single Go import + a stale go.mod replace-comment
+ README require example off the dead github.com/Molecule-AI/ identity
and onto the vanity host go.moleculesai.app, owned by us. Adds a
structural lint test that walks every *.go / *.mod / Dockerfile / *.md
and rejects future re-introduction of either the literal
github.com/Molecule-AI/ string or the historical
Molecule-AI/molecule-monorepo path.

Why a vanity host and not git.moleculesai.app/molecule-ai/...:
the latter just relocates the lock-in. 2026-05-06 didn't teach us
'Gitea > GitHub'; it taught us 'vendor URLs in source code are a future
incident'. With go.moleculesai.app, the next SCM migration edits one
config (the responder) instead of every import statement. See
internal#71 for the full rationale + the alternatives rejected.

Smoke-validation for this migration sweep — chosen as the first PR
because it has the smallest blast radius (1 import line).
Following PRs: molecule-cli, molecule-controlplane, molecule-core
(parallel after this lands).

Test plan:
- go build ./... — clean
- go test ./... — internal/ghidentity green
- TestNoLegacyGitHubImportPaths — passes on clean tree, FAILS on
  injected canary string (mutation-tested before commit)

Open dependency: go.moleculesai.app responder must be deployed before
external 'go install go.moleculesai.app/plugin/gh-identity@latest'
works. Internal builds are unaffected (self-referential module path,
no external fetch needed). Responder code prepared in worker.js;
deploy tracked separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:23:51 +00:00
.github/workflows feat(plugin): gh-identity — per-agent attribution via env injection + gh wrapper 2026-04-23 20:38:45 -07:00
examples feat(plugin): gh-identity — per-agent attribution via env injection + gh wrapper 2026-04-23 20:38:45 -07:00
internal/ghidentity feat(plugin): gh-identity — per-agent attribution via env injection + gh wrapper 2026-04-23 20:38:45 -07:00
pluginloader fix(post-suspension): vanity import path go.moleculesai.app/plugin/gh-identity (closes molecule-ai/internal#71 phase 2) 2026-05-07 22:23:51 +00:00
scripts feat(plugin): gh-identity — per-agent attribution via env injection + gh wrapper 2026-04-23 20:38:45 -07:00
.gitignore feat(plugin): gh-identity — per-agent attribution via env injection + gh wrapper 2026-04-23 20:38:45 -07:00
go.mod fix(post-suspension): vanity import path go.moleculesai.app/plugin/gh-identity (closes molecule-ai/internal#71 phase 2) 2026-05-07 22:23:51 +00:00
go.sum feat(plugin): gh-identity — per-agent attribution via env injection + gh wrapper 2026-04-23 20:38:45 -07:00
known-issues.md fix(post-suspension): migrate github.com/Molecule-AI refs to git.moleculesai.app (Class G #168) 2026-05-07 13:03:41 -07:00
README.md fix(post-suspension): vanity import path go.moleculesai.app/plugin/gh-identity (closes molecule-ai/internal#71 phase 2) 2026-05-07 22:23:51 +00:00

molecule-ai-plugin-gh-identity

Injects per-agent identity into workspace env so every gh CLI call carries agent attribution — without needing a distinct GitHub account per agent.

Problem

All agents in a Molecule fleet share one GitHub PAT. When an agent runs:

gh issue create --assignee @me ...
gh pr comment ...

@me resolves to the PAT owner (the CEO). Every issue, PR, and comment gets attributed to one person, making audit impossible and flooding that person's notifications. See molecule-core#1957.

GitHub's identity model doesn't scale the way agent fleets do: creating N machine users requires N emails + seats; creating N GitHub Apps requires N manual UI round-trips. Neither is batch-generatable.

Approach

Work around the identity model with a convention, enforced by a tiny shell wrapper and this plugin's env injection.

  1. Plugin injects per-workspace env: MOLECULE_AGENT_ROLE, MOLECULE_OWNER, MOLECULE_ATTRIBUTION_BADGE.
  2. The workspace base image ships a gh wrapper (/usr/local/bin/gh) that reads $MOLECULE_AGENT_ROLE and:
    • prepends an attribution block to every issue comment / pr comment / issue create --body / pr create --body
    • rewrites --assignee @me to --assignee $MOLECULE_OWNER (or strips it entirely)
    • emits an audit line to /var/log/molecule-gh.ndjson
  3. A git wrapper does the same for Co-authored-by: on commits.

The wrapper script is shipped embedded in the plugin (wrapper.sh) and installed by each workspace-template's install.sh when the plugin is active. Plugin → env injection; template → file write.

What this plugin is NOT

  • NOT a GitHub App installer. Existing molecule-ai-plugin-github-app-auth handles App-based auth; this plugin is additive and does not conflict.
  • NOT a machine-user provisioning tool. There are no distinct GitHub identities; attribution is text-based.
  • NOT a per-agent rate limiter or cost accounter (future work; see #1957 follow-ups).

Env vars injected

Name Source Example
MOLECULE_AGENT_ROLE workspace metadata (role field) PMM-Lead
MOLECULE_OWNER plugin config (role → owner map) HongmingWang-Rabbit
MOLECULE_ATTRIBUTION_BADGE computed 🤖 [Agent: PMM-Lead · ws-a0689c35]
MOLECULE_GH_WRAPPER_SHA computed hash of wrapper.sh for version pinning

Plugin manifest (v1)

This plugin ships as a v1 plugin (matching molecule-ai-plugin-github-app-auth). Migration to plugin-architecture-v2 happens in phase 6 of that plan. The v1 shape here is intentionally structured so v2 migration is mostly a manifest rename:

  • EnvMutator.MutateEnv → v2's contributions.env + hooks.env_refresh
  • Role→owner map in config.yaml → v2's spec.config
  • Wrapper script shipping → v2's contributions.files (new axis)

Install (v1)

Monorepo side:

manifest.json:plugins += {name: "gh-identity", repo: "Molecule-AI/molecule-ai-plugin-gh-identity", ref: "main"}
workspace-server/go.mod: require go.moleculesai.app/plugin/gh-identity
workspace-server/cmd/server/main.go: pluginloader.BuildRegistry()

Env (operator):

MOLECULE_GH_IDENTITY_CONFIG_FILE=/path/to/config.yaml

Config

# config.yaml — role → owner map (used for `@me` rewrite)
roles:
  PMM-Lead:     { owner: HongmingWang-Rabbit }
  Dev-Lead:     { owner: HongmingWang-Rabbit }
  Research-Lead:{ owner: HongmingWang-Rabbit }
  default:      { owner: HongmingWang-Rabbit }

Capabilities requested (v2 forward-compat)

When v2 enforcement lands, this plugin will declare:

  • workspace:env_inject — required
  • workspace:file_write:/usr/local/bin/gh — required (via template install.sh)
  • audit:emit — required
  • network_egress:api.github.com — required (wrapper makes API calls via real gh)

No broader capabilities. In particular: no secret access (PAT is shared and platform-managed, not in plugin scope).

  • molecule-core#1957 — agent identity collapse (this plugin's driver)
  • molecule-core#1933 — GH_TOKEN refresh (separate concern; handled by github-app-auth plugin)
  • internal product/plugin-architecture-v2.md — target arch for v2 migration