GitHub Plugin
The GitHub plugin uses one GitHub App permission envelope for the repositories Junior can reach. Junior acts as the App installation for reads, workflow dispatches, issue and pull request maintenance, and Git branch pushes. Human OAuth remains reserved for operations whose GitHub meaning is personal, such as submitting a pull request review.
Install
Section titled “Install”Install the plugin package alongside @sentry/junior:
pnpm add @sentry/junior @sentry/junior-githubRuntime setup
Section titled “Runtime setup”Add the GitHub plugin factory to the plugin set exported from plugins.ts. The factory registers the GitHub manifest,
bundled skills, and Git commit attribution hooks together.
import { defineJuniorPlugins } from "@sentry/junior";import { githubPlugin } from "@sentry/junior-github";
export const plugins = defineJuniorPlugins([ githubPlugin({ botNameEnv: "GITHUB_APP_BOT_NAME", botEmailEnv: "GITHUB_APP_BOT_EMAIL", }),]);Junior requests read-level installation tokens for read traffic. Allowlisted writes receive a repository-scoped token with the complete permission envelope approved on the GitHub App installation, so a branch push and the following pull request operation cannot receive mismatched permissions. Unsupported writes are still denied by the egress policy instead of borrowing a user token.
You can optionally declare appPermissions when registering the plugin. Junior uses that declaration to advertise capabilities and avoid an installation lookup when downscoping read tokens; it does not downscope write tokens. Keep the declaration aligned with the permissions approved in the GitHub App settings.
Configure environment variables
Section titled “Configure environment variables”Set these values in the host environment:
| Variable | Required | Purpose |
|---|---|---|
GITHUB_APP_ID | Yes | GitHub App identity. |
GITHUB_APP_CLIENT_ID | Yes | GitHub App OAuth client id for user-token auth. |
GITHUB_APP_CLIENT_SECRET | Yes | GitHub App OAuth client secret for user-token auth. |
GITHUB_APP_PRIVATE_KEY | Yes | GitHub App signing key. |
GITHUB_INSTALLATION_ID | Yes | Repository or organization installation target. |
GITHUB_APP_BOT_NAME | Yes | Git author and committer display name. |
GITHUB_APP_BOT_EMAIL | Yes | App bot noreply email used for Git attribution and work ownership. |
GITHUB_WEBHOOK_SECRET | No | Webhook signing secret for PR watches and outcome reporting. |
GITHUB_INSTALLATION_ID selects the GitHub App installation for the deployment.
GITHUB_APP_BOT_EMAIL uses the GitHub noreply format
<bot-user-id>+<app-slug>[bot]@users.noreply.github.com. Get the bot user id
from https://api.github.com/users/<app-slug>%5Bbot%5D. Junior derives the
App bot login after the + when classifying pull requests and issues for
outcome reporting.
Vercel example:
vercel env add GITHUB_APP_ID productionvercel env add GITHUB_APP_CLIENT_ID productionvercel env add GITHUB_APP_CLIENT_SECRET productionvercel env add GITHUB_INSTALLATION_ID productionvercel env add GITHUB_APP_BOT_NAME productionvercel env add GITHUB_APP_BOT_EMAIL productionvercel env add GITHUB_APP_PRIVATE_KEY production --sensitive < ./github-app-private-key.pemvercel env add GITHUB_WEBHOOK_SECRET productionRun migrations
Section titled “Run migrations”After installing or upgrading the GitHub plugin, run its packaged SQL migration from the deployed app environment:
pnpm exec junior upgradeThis creates the junior_github_pull_requests and junior_github_issues
projections used by webhook ingestion and the /system outcome report.
Create the GitHub App
Section titled “Create the GitHub App”Create and install a GitHub App before you verify GitHub workflows:
-
Open GitHub App settings and create a new app.
-
Generate a private key and store the downloaded
.pemfile securely. -
Grant repository permissions for:
- Actions: Read and write
- Checks: Read
- Issues: Read and write
- Contents: Read and write
- Pull requests: Read and write
- Workflows: Write
- Metadata: Read
-
If Junior should watch pull requests or report pull request and issue outcomes, enable webhooks and set the webhook URL to:
https://<your-domain>/api/webhooks/github -
Set the webhook secret to the same value as
GITHUB_WEBHOOK_SECRET, then subscribe the app to these repository events:- Check suite
- Issues
- Issue comment
- Pull request
- Pull request review
- Pull request review comment
-
Install the app on the repository or organization Junior should access.
-
Copy the App ID, OAuth client ID/secret, installation ID, bot name, bot noreply email, and, if you enabled webhooks, the webhook secret into your deployment environment.
Do not lower the GitHub App permission itself to read-only if Junior should create issues, push branches, or open pull requests. Junior scopes write tokens to the target repository and keeps write operations constrained by the egress allowlist.
Git smart-HTTP push classification is repository-scoped, not branch-scoped. It does not independently identify Junior-managed branches or detect force updates or ref deletion. Protect important branches in GitHub and install the App only on repositories where Junior may push.
If your team works across multiple repositories, have users include owner/repo in their GitHub request whenever the target is not obvious from the conversation.
That only helps when those repositories are covered by the same GitHub App installation ID.
Watch pull request events
Section titled “Watch pull request events”When GITHUB_WEBHOOK_SECRET is configured, github_createPullRequest and github_getPullRequest return a subscribable pull request resource. Junior can then subscribe the current Slack conversation to high-signal PR events for either a newly created or existing PR when watching it serves the user’s request.
Subscribed events run headlessly as Junior’s resource-event system actor.
They can use repository-scoped installation credentials to commit and push a
follow-up fix to the watched pull request without borrowing the subscriber’s
OAuth identity. Actions that represent human judgment, such as submitting a
review, still require explicit delegated user authorization.
Supported GitHub webhook deliveries become these Junior resource events:
| GitHub delivery | Junior event types |
|---|---|
check_suite completed | checks.failed, checks.recovered |
issue_comment created on a PR | comment.created |
pull_request_review submitted | review.approved, review.changes_requested, review.commented |
pull_request_review_comment created | review_comment.created |
pull_request closed | state.merged, state.closed_unmerged |
state.merged and state.closed_unmerged complete the subscription after Junior accepts the event. Other supported events keep the watch active until the subscription expires, is cancelled, or reaches its configured TTL.
Webhook events are delivered as normal queued conversation messages. They do not interrupt active work, bypass Slack routing, or act as user-authored commands. Junior uses the subscription intent to decide whether to reply, take a follow-up action, or stay silent.
The GitHub plugin classifies a pull request or issue as Junior-owned on its
signed opened event when the author matches the bot login derived from
GITHUB_APP_BOT_EMAIL and the opening body contains Junior’s session footer.
It keeps tracking that projection through later lifecycle events even if the
body changes. The /system dashboard reports pull request creation, merge,
closure, merge-rate, and time-to-merge summaries alongside issue creation,
closure, open-count, and time-to-close summaries for 7-, 30-, and 90-day
windows. This reporting is independent of conversation subscriptions and never
stores pull request or issue bodies.
When a tracked pull request merges, Junior reads its commit list with the
GitHub App installation credential. A merged pull request is Junior-only when
every commit’s Git author matches the configured bot login or bot email. If any
commit has another author, the report classifies the pull request as mixed.
Older records, empty commit lists, and lookups that fail remain unknown; the
pull request outcome is still recorded. The headline Junior-only merge rate
excludes unknown records. Junior stores only that classification, not commit
SHAs, author identities, or email addresses.
The pull request projection also keeps the native Junior conversation ids from
bot-written session footers in a deduplicated text[]. These are opaque
association values, not foreign keys, and later bot-authored footer updates add
to the array without removing earlier ids. Junior does not store conversation
content in the GitHub projection.
If GitHub delivers a terminal event before its opening event, the plugin can establish the same ownership from the bot-and-footer marker on that terminal payload. This recovery applies to both pull requests and issues. A later stale opening event cannot regress the recorded outcome.
Verify
Section titled “Verify”Run a real GitHub workflow in the chat surface where people will use it:
Create a GitHub issue in owner/repo titled "Junior GitHub plugin check" with body "Verification run"Then confirm:
- The issue is created in the expected repository.
- The author is the GitHub App bot, and the body includes
Requested byattribution for the verified runtime actor. - A follow-up GitHub request can update or comment on the same issue without asking the user to authorize GitHub or handle tokens manually.
- A pushed branch can be turned into a draft PR with
github_createPullRequestusing explicitrepo,head, andbasevalues. - After that PR merges,
/systemreports it as a Junior-only or mixed merge.
For code changes, a local git commit does not call GitHub. The GitHub write happens when Junior pushes the branch. The App installation requires Contents: write; grant it Workflows: write when Junior may change files under .github/workflows. Creating the PR after the branch exists is a separate pull-request write operation, but it uses the same repository-scoped write credential.
To verify PR event watches, create a PR through Junior in Slack and ask Junior to keep an eye on CI, review changes, or merge state. Trigger one configured GitHub webhook event, then confirm GitHub reports a successful delivery to /api/webhooks/github and Junior handles the event in the original Slack conversation according to the watch intent.
Security model
Section titled “Security model”- Junior mints GitHub App installation and user-to-server tokens on the host, not in the sandbox.
- When the GitHub skill runs authenticated
ghorgitcommands, sandbox traffic toapi.github.comandgithub.comis forwarded through Junior for host-side auth. - App-readable requests use installation tokens downscoped to read. Allowlisted workflow dispatch, issue, pull request, and branch writes use repository-scoped installation tokens carrying the complete installed App permission envelope. GitHub account identity checks and human review operations use user-to-server tokens.
- GitHub App user-to-server tokens do not use OAuth scopes as their permission model. Their effective access comes from the App permissions, installation scope, and requesting user’s access.
- The GitHub App installation determines which repositories are reachable, and repository write grants narrow issued tokens to the parsed target repository.
- The host-side lease is bounded by the sandbox session and token expiry. It is not exposed as reusable long-lived auth inside the sandbox.
- GitHub webhooks are accepted only when the
X-Hub-Signature-256header matchesGITHUB_WEBHOOK_SECRET. - Resource event subscriptions are conversation-scoped. Core owns subscription records, dedupe, TTL, and mailbox delivery; the GitHub plugin owns signature verification, provider normalization, and its pull request and issue outcome projections.
- Resource-event turns do not inherit a subscriber’s user credential. Bot-owned issue, pull request, and smart-HTTP push operations use scoped installation credentials; human-owned operations still enter the normal authorization flow.
- The write boundary is the App installation scope, the single-repository token scope, and Junior’s endpoint allowlist.
appPermissionsdeclarations do not narrow write tokens.
Failure modes
Section titled “Failure modes”Access deniedfrom GitHub: the app is not installed on the target repository or organization. Install the app on that target, then retry.Bad credentialsor signing errors:GITHUB_APP_PRIVATE_KEYdoes not match the App ID. Upload the private key generated for the same app asGITHUB_APP_ID.- PR creation works but Junior never offers to watch the PR:
GITHUB_WEBHOOK_SECRETis missing from the deployment environment. Set it, redeploy, and create a new PR through Junior. - GitHub webhook delivery returns
401: the webhook secret in GitHub App settings does not matchGITHUB_WEBHOOK_SECRET, or GitHub did not sendX-Hub-Signature-256. Update the app webhook secret and retry the delivery. - GitHub webhook delivery returns
202 Ignored: the delivery was signed correctly but does not map to a supported PR watch event. Use one of the configured event types above. - GitHub delivery succeeds but no Slack follow-up appears: confirm the original conversation has an active resource event subscription for that PR and event type. A successful webhook alone does not create a subscription.
- Missing repository context: Junior could not determine which repository to use. Include
owner/repodirectly in the GitHub request, or configure a default GitHub repository for that thread, and retry. - A
403response that says to usegithub_createIssueorgithub_createPullRequestis a Junior routing denial, not evidence of missing App permissions. Retry with the named tool. - Private OAuth prompt for a human-identity operation such as a pull request review: the actor has not authorized the GitHub App yet, or the stored user-to-server token expired. Complete the private authorization prompt; do not paste personal access tokens into the chat or sandbox.
- Permission-style failures during issue or pull request workflows: the GitHub App lacks the required permission or installation scope. Update the app permissions or install target, then retry.
- Fork creation is outside the write allowlist. Routine PR creation should push a branch explicitly and use
github_createPullRequestinstead of creating a fork.
Next step
Section titled “Next step”Read Plugin Auth & Context for the public auth and target-context model.