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>
4.4 KiB
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.
- Plugin injects per-workspace env:
MOLECULE_AGENT_ROLE,MOLECULE_OWNER,MOLECULE_ATTRIBUTION_BADGE. - The workspace base image ships a
ghwrapper (/usr/local/bin/gh) that reads$MOLECULE_AGENT_ROLEand:- prepends an attribution block to every
issue comment/pr comment/issue create --body/pr create --body - rewrites
--assignee @meto--assignee $MOLECULE_OWNER(or strips it entirely) - emits an audit line to
/var/log/molecule-gh.ndjson
- prepends an attribution block to every
- A
gitwrapper does the same forCo-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-authhandles 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'scontributions.env+hooks.env_refresh- Role→owner map in
config.yaml→ v2'sspec.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— requiredworkspace:file_write:/usr/local/bin/gh— required (via template install.sh)audit:emit— requirednetwork_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).
Related
- 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