Sitelet https://github.com/Resnovas/.github/pull/16
Skip to content

ci(house): authenticate house workflows as the Resnovas Bot GitHub App - #16

Merged
TGTGamer merged 2 commits into
claude/smc-63-more-settingsfrom
claude/smc-121-bot-app
Sep 26, 2026
Merged

TGTGamer merged 2 commits into
claude/smc-63-more-settingsfrom
claude/smc-121-bot-app

Conversation

@TGTGamer

@TGTGamer TGTGamer commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

What changed

  • smartcloud template (templates/.github/workflows/smartcloud.yml): mints a Resnovas Bot token with actions/create-github-app-token (pinned to the v3.2.0 SHA, owner: Resnovas), using the RESNOVAS_BOT_APP_ID variable and RESNOVAS_BOT_PRIVATE_KEY secret. It replaces secrets.ACCESS_TOKEN. The token reaches only the repository and Resnovas/.github.
  • Graphify refresh (graphify.yml): mints its own token for the calling repository only, with contents and pull request write access, and commits with sign-commits: true, so GitHub signs the refresh commit. The sign-off names the app's bot, which is looked up from app-slug.
  • Caller (house-graphify.yml): passes app-id and private-key (never on pull requests) instead of ACCESS_TOKEN.
  • resnovas-smartcloud[bot] (the app, ID 5089542) joins the trusted bots. The preset comments and the README's token section now describe the app.

Why

The PAT's rate limit was shared across every run. It could not create check runs, and its sync and Graphify commits were unsigned. App tokens are short-lived and scoped. GitHub signs commits made with them through the API, so required signatures need no bypass (SMC-48).

Decisions

  • Least privilege: fork and Dependabot runs get no secrets, so they mint nothing and fall back to github.token. A failed mint (continue-on-error) falls back the same way instead of failing the run. No token is minted in a job that runs pull request code. Everything in-repo, such as CI and the Graphify check, keeps github.token.
  • ACCESS_TOKEN: removed from the workflows. The reusable Graphify workflow still accepts the token secret as a deprecated fallback, so repositories that haven't synced the new caller keep working. The README says to delete the secret once every repository has synced.
  • Merge order: merge this PR before the synced caller reaches other repositories. The new caller passes an app-id input that the old graphify.yml@main doesn't declare.

Closes SMC-121

Stacks on #12. Companion: Resnovas/smartcloud#649 (signed sync commits and the self-hosted workflow).


Note

Medium Risk
Org-wide CI auth changes how sync, settings, and Graphify refresh push and sign commits; callers must merge after graphify.yml exposes the new inputs or refresh runs can break.

Overview
House automation stops using the organisation ACCESS_TOKEN PAT and instead mints short-lived Resnovas Bot GitHub App tokens via actions/create-github-app-token, using RESNOVAS_BOT_APP_ID and RESNOVAS_BOT_PRIVATE_KEY.

The smartcloud workflow (template and rendered copy) scopes each mint to the current repo plus Resnovas/.github, skips minting on forks/Dependabot or when credentials are missing, and falls back to github.token if minting fails (continue-on-error). Graphify refresh gains optional app-id / private-key on the reusable workflow, mints a repo-only token when the graph is stale, sets author/committer to the app bot, and opens refresh PRs with sign-commits: true and API commits so GitHub signs them. house-graphify callers pass the app inputs instead of ACCESS_TOKEN; the old token secret remains as a deprecated fallback for repos not yet synced.

README and smartcloud/house.yml comments describe the app, required permissions, and retiring ACCESS_TOKEN after downstream sync.

Reviewed by Cursor Bugbot for commit 48461f3. Bugbot is set up for automated code reviews on this repo. Configure here.

@linear-code

linear-code Bot commented Sep 26, 2026

Copy link
Copy Markdown

SMC-121

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Authenticate house workflows with the Resnovas Bot GitHub App

✨ Enhancement ⚙️ Configuration changes 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Replace shared PAT authentication with short-lived, repository-scoped Resnovas Bot App tokens.
• Sign Graphify refresh commits and preserve workflow-token fallback for restricted or unsynced
 callers.
• Document App installation, permissions, migration, and trusted-bot governance.
Diagram

graph TD
  A["Workflow event"] --> B{"Secrets allowed?"} -->|Yes| C["Token action"] --> D["Scoped app token"] --> F["Smartcloud sync"] --> H["Signed commits"]
  B -->|No or mint failure| E["Workflow token"] --> F
  D --> G["Graphify refresh"] --> H
  E --> G
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Retain the shared personal access token
  • ➕ Requires fewer workflow changes
  • ➕ Avoids GitHub App installation and key management
  • ➖ Shares one user's rate limit across repositories
  • ➖ Uses a long-lived credential with broader exposure
  • ➖ Cannot provide the desired App identity and GitHub-signed automation commits
2. Sign commits with a managed GPG key
  • ➕ Can satisfy signature requirements without API commit signing
  • ➕ Allows a custom automation identity
  • ➖ Introduces another long-lived private key
  • ➖ Requires secure signing-key distribution and rotation
  • ➖ Does not solve shared PAT rate limits or token scoping

Recommendation: Use the PR's GitHub App approach. Short-lived installation tokens, explicit repository scope, job-specific permissions, API-backed signatures, and a temporary legacy fallback address both security and rollout compatibility better than retaining a PAT or managing a separate signing key.

Files changed (8) +148 / -40

Documentation (1) +14 / -6
README.mdDocument GitHub App authentication and PAT retirement +14/-6

Document GitHub App authentication and PAT retirement

• Updates repository setup and token guidance for the Resnovas Bot installation, credential names, scopes, permissions, signing behavior, and rate limits. It also documents restricted-run fallback behavior and the temporary ACCESS_TOKEN migration window.

README.md

Other (7) +134 / -34
graphify.ymlMint scoped App tokens for signed Graphify refreshes +52/-4

Mint scoped App tokens for signed Graphify refreshes

• Adds optional GitHub App credentials to the reusable workflow and mints a repository-scoped token only when a stale graph requires a refresh. Refresh commits now use the App bot identity and GitHub API signing, while the deprecated token secret and workflow token remain ordered fallbacks.

.github/workflows/graphify.yml

house-graphify.ymlPass GitHub App credentials to Graphify +7/-4

Pass GitHub App credentials to Graphify

• Replaces the caller's ACCESS_TOKEN forwarding with the organization App ID and private key. The private key remains unavailable to pull request executions.

.github/workflows/house-graphify.yml

smartcloud.ymlAuthenticate smartcloud with a repository-scoped App token +32/-9

Authenticate smartcloud with a repository-scoped App token

• Computes the permitted repository list and mints a Resnovas Bot token for the current repository plus Resnovas/.github. Fork, Dependabot, and failed-mint paths fall back to github.token instead of failing.

.github/workflows/smartcloud.yml

house.ymlTrust the Resnovas Bot account +1/-1

Trust the Resnovas Bot account

• Adds resnovas-smartcloud[bot] to the rendered house configuration's trusted automation accounts.

house.yml

house.ymlAdd App bot trust and authentication guidance +3/-3

Add App bot trust and authentication guidance

• Adds the Resnovas Bot account to the shared trusted-bot policy. Comments now identify GitHub App minting and workflow-token fallback as the authentication model for settings and Graphify automation.

smartcloud/house.yml

house-graphify.ymlTemplate Graphify callers with App credentials +7/-4

Template Graphify callers with App credentials

• Updates the managed caller template to pass the organization App ID and conditionally expose the private key outside pull requests. Newly synced repositories no longer forward ACCESS_TOKEN.

templates/.github/workflows/house-graphify.yml

smartcloud.ymlTemplate scoped App authentication for smartcloud +32/-9

Template scoped App authentication for smartcloud

• Adds repository-scope calculation and SHA-pinned GitHub App token minting to the managed smartcloud template. Restricted contexts and minting failures continue with the workflow token.

templates/.github/workflows/smartcloud.yml

@qodo-code-review

qodo-code-review Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Secretless runs attempt token minting ✗ Dismissed 🔴 Required 📎 Requirement gap ☼ Reliability
Description
The smartcloud mint condition checks only the pull-request fork flag and Dependabot actor, without
checking whether the App ID and private key exist. A regular push, schedule, or manual run in an
external repository without organization credentials invokes the token action with empty inputs
before its tolerated failure falls back to github.token.
Code

.github/workflows/smartcloud.yml[R54-57]

+      - name: Mint the Resnovas Bot token
+        id: app
+        if: github.event.pull_request.head.repo.fork != true && github.actor != 'dependabot[bot]'
+        continue-on-error: true
Evidence
Rule 3590094 says external and secretless runs must not attempt App-token minting. The added
condition excludes fork pull requests and Dependabot but remains true for other event types and
external copies where the organization variable and secret are absent.

Fall back safely for fork, Dependabot, and external runs
.github/workflows/smartcloud.yml[53-62]
templates/.github/workflows/smartcloud.yml[53-62]
README.md[126-128]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Smartcloud attempts App-token creation during secretless non-Dependabot runs because its condition does not test credential availability.

## Fix Focus Areas
- .github/workflows/smartcloud.yml[54-62]
- templates/.github/workflows/smartcloud.yml[54-62]

## Recommended Fix
Expose a non-secret credential-availability flag at job scope and include it in the mint step condition so the action runs only when both the App ID and private key are present. Preserve the existing fork and Dependabot exclusions and continue passing `github.token` to smartcloud when minting is skipped.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Automation cannot mint bot tokens ✗ Dismissed 🔴 Required 📎 Requirement gap ≡ Correctness
Description
The token steps pass the unsupported client-id input to actions/create-github-app-token@v3.2.0
instead of its required app-id input. When either workflow needs App authentication, token
creation fails and continue-on-error sends smartcloud and Graphify refreshes through their
fallback credentials instead.
Code

.github/workflows/smartcloud.yml[61]

+          client-id: ${{ vars.RESNOVAS_BOT_APP_ID }}
Evidence
Rule 3590091 requires these workflows to provide the documented App ID to the pinned token action.
Both smartcloud copies and the reusable Graphify workflow instead use an unsupported input name,
while the Graphify invocation also omits the required owner: Resnovas configuration.

Mint Resnovas Bot GitHub App tokens securely
.github/workflows/smartcloud.yml[58-63]
templates/.github/workflows/smartcloud.yml[58-63]
.github/workflows/graphify.yml[105-111]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The pinned token action requires an `app-id` input, but the smartcloud and Graphify workflows pass `client-id`, so App-token creation cannot succeed.

## Fix Focus Areas
- .github/workflows/smartcloud.yml[58-63]
- templates/.github/workflows/smartcloud.yml[58-63]
- .github/workflows/graphify.yml[105-111]

## Recommended Fix
Replace each `client-id` key with `app-id` while retaining the existing `RESNOVAS_BOT_APP_ID` value. For Graphify, also set `owner: Resnovas` and explicitly scope `repositories` to the calling repository.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Identity lookup can block refreshes ✗ Dismissed 🟠 Recommended 🐞 Bug ☼ Reliability
Description
The Commit identity step calls gh api to populate committer and author, but
create-pull-request explicitly ignores both inputs when sign-commits is enabled. Any transient
failure resolving the bot user now fails the job before Open refresh pull request, even though the
token alone determines the signed commit identity.
Code

.github/workflows/graphify.yml[R121-123]

+          if [ -n "$GH_TOKEN" ] && [ -n "$SLUG" ]; then
+            id=$(gh api "/users/${SLUG}%5Bbot%5D" --jq .id)
+            echo "committer=${SLUG}[bot] <${id}+${SLUG}[bot]@users.noreply.github.com>" >> "$GITHUB_OUTPUT"
Evidence
The workflow performs the user lookup and passes its output as committer and author, while
create-pull-request's commit-signing documentation states that both inputs are ignored whenever
sign-commits: true is used.

.github/workflows/graphify.yml[113-139]
🌐 The action documents that enabling sign-commits causes it to ignore the committer and author inputs.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Graphify refresh performs a fatal bot-user API lookup whose outputs are ignored when create-pull-request uses signed API commits.

## Fix Focus Areas
- .github/workflows/graphify.yml[113-139]

## Recommended Fix
Remove the `Commit identity` step and the `committer` and `author` inputs from `Open refresh pull request`. Keep the app token and `sign-commits: true`, which make create-pull-request derive the signed commit identity from the authenticating bot token.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Secretless runs still refresh graphs ✗ Dismissed 🟠 Recommended 📎 Requirement gap ⛨ Security
Description
Open refresh pull request selects github.token when neither an App token nor the deprecated
token is available. On a non-pull-request run of an external repository's default branch, the
write-enabled refresh job can therefore create the Graphify commit and pull request instead of
disabling that privileged feature.
Code

.github/workflows/graphify.yml[132]

+          token: ${{ steps.app.outputs.token || secrets.token || github.token }}
Evidence
Rule 3590094 requires Graphify refresh commits to be disabled when organization credentials are
unavailable. The reusable workflow instead grants write permissions, skips minting when
HAS_APP_KEY is false, and explicitly falls back to github.token for the refresh operation.

Fall back safely for fork, Dependabot, and external runs
.github/workflows/graphify.yml[68-74]
.github/workflows/graphify.yml[101-105]
.github/workflows/graphify.yml[128-136]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Graphify refresh continues with `github.token` when no App or migration token exists, although secretless external runs must disable refresh commits.

## Fix Focus Areas
- .github/workflows/graphify.yml[128-132]

## Recommended Fix
Gate the refresh pull-request step on a successfully minted App token or the explicitly supported deprecated migration token. Keep `github.token` for the read-only Graphify check, but do not use it to create refresh commits in secretless runs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

No findings for this group

Grey Divider

Context sources
✅ Compliance rules (platform): 1 rule
✅ Web pages:
  +12 more
Review mode: ⚖️ Balanced: This is a security-sensitive CI authentication change spanning multiple workflows, token scopes, fallback paths, and signed-commit behavior.

Grey Divider

Tip of the day
💡 Did you know, you can type 'qodo, fix this' on a finding and the fix lands right on your PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread .github/workflows/smartcloud.yml
Comment thread .github/workflows/graphify.yml
Comment thread .github/workflows/smartcloud.yml
Comment thread .github/workflows/graphify.yml

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agentic security review completed on the current head. One high finding: the smartcloud App token lists Resnovas/.github in the same mint as the calling repository without permission-* downscope, so the house source of truth inherits the App's full write permissions.

Open in Web View Automation 

Sent by Cursor Security Agent: Security Reviewer

Comment on lines +47 to +64
- name: Repositories the token may reach
id: scope
run: |
name="${GITHUB_REPOSITORY#*/}"
if [ "$name" = .github ]; then echo "repositories=.github"; else echo "repositories=$name,.github"; fi >> "$GITHUB_OUTPUT"

# A failed mint falls back to the workflow token rather than failing the run.
- name: Mint the Resnovas Bot token
id: app
if: github.event.pull_request.head.repo.fork != true && github.actor != 'dependabot[bot]'
continue-on-error: true
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
# GitHub accepts the App ID wherever the client ID is asked for.
client-id: ${{ vars.RESNOVAS_BOT_APP_ID }}
private-key: ${{ secrets.RESNOVAS_BOT_PRIVATE_KEY }}
owner: Resnovas
repositories: ${{ steps.scope.outputs.repositories }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Agentic Security Review
Severity: HIGH

This mint lists the calling repository and Resnovas/.github together and omits permission-* inputs, so actions/create-github-app-token issues an installation token with every permission the App has on both repos (Administration, Contents, Workflows, Checks, Issues, Pull requests, and related settings).

The previous PAT was documented as write on synced repos and read on .github. Installation tokens cannot split permissions per repository, so .github now receives the same write set as the caller. Same-repo pull_request runs still mint (forks and Dependabot are skipped), and the token is passed to unpinned resnovas/smartcloud@v2. A workflow change on a leaf repo, or a retagged v2, can therefore mutate the organisation house source of truth, not only the calling repository.

The Graphify refresh job in this PR already mints for the current repository only with explicit contents and pull-request write. For smartcloud, keep a write-scoped mint for the caller and a separate contents/metadata read mint for .github (or equivalent), and pin the smartcloud action to a SHA.

Impact: A less-privileged actor with write on a synced repository, or control of the unpinned v2 tag, can use this token to change house presets, trusted-bot lists, and synced templates in Resnovas/.github.

Fix in Cursor Fix in Web

Reviewed by Cursor Security Reviewer for commit 9a5d4ba. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid. An installation token can't split permissions per repository, so the shared mint gives Resnovas/.github the caller's write set. Jonathan's decision: this PR merges as it is (it replaces a PAT that had org-wide reach), and the fix is SMC-128 (Urgent), done right after this landing. It adds a write-scoped mint for the calling repository only, a separate contents:read mint for Resnovas/.github, and github.token for in-repo work. Leaving this thread open until SMC-128 lands. Pinning resnovas/smartcloud to a SHA has to wait for the first v2 release.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: high. Left a non-blocking comment and did not approve: Cursor Security Agent reported an unresolved high finding on the smartcloud App token scope, and this authentication change is above the medium approval threshold. Human review is needed; no reviewers were assigned because the only CODEOWNER is the PR author.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

@TGTGamer
TGTGamer added this pull request to stack #23 September 26, 2026 22:47
The smartcloud template mints a Resnovas Bot token (RESNOVAS_BOT_APP_ID, RESNOVAS_BOT_PRIVATE_KEY) with a SHA-pinned actions/create-github-app-token, scoped to the repository and Resnovas/.github, instead of passing the ACCESS_TOKEN PAT. Forks and Dependabot mint nothing and fall back to the workflow token.

The Graphify refresh mints its own token, only for the calling repository with contents and pull request access, and commits with sign-commits so GitHub signs the refresh commit, signed off as the app's bot. The reusable workflow keeps the token secret as a deprecated fallback so callers not yet synced keep working. resnovas-smartcloud[bot] joins the trusted bots, and the README documents the app.

Refs SMC-121

Signed-off-by: Jonathan Stevens <jonathan@resnovas.com>
Review fix. The smartcloud job tried the mint with empty inputs wherever the
app's ID or key was missing, as in a repository outside the organisation, and
relied on continue-on-error to fall back. The mint now runs only when both
are set, as the Graphify refresh already does.

Refs: SMC-121

Signed-off-by: Jonathan Stevens <jonathan@resnovas.com>
@TGTGamer
TGTGamer force-pushed the claude/smc-121-bot-app branch from 9a5d4ba to 48461f3 Compare September 26, 2026 23:42
@TGTGamer
TGTGamer merged commit 0dcdb20 into main Sep 26, 2026
8 of 12 checks passed
@TGTGamer
TGTGamer deleted the claude/smc-121-bot-app branch September 26, 2026 23:44

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.


Graphify review — findings

Migrates the house workflows from the ACCESS_TOKEN PAT to the Resnovas Bot GitHub App, minting a short-lived, repository-scoped token via actions/create-github-app-token from RESNOVAS_BOT_APP_ID and RESNOVAS_BOT_PRIVATE_KEY. The smartcloud token is scoped to the calling repo plus Resnovas/.github, while the Graphify refresh mints a contents/pull-requests token only when the graph is stale, commits through the API so GitHub signs the commit, and attributes the commit to the bot identity. Falls back to the workflow token when no app key is present or the mint fails (forks, Dependabot, out-of-org repos), keeping token/ACCESS_TOKEN as a deprecated path for unsynced callers.

Worth a look

  • Smartcloud app token mint uses unsupported input name — .github/workflows/smartcloud.yml:62 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • GitHub App token action uses unsupported input name — .github/workflows/graphify.yml:108 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Smartcloud App token is minted with all installation permissions — .github/workflows/smartcloud.yml:57 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Reusable Graphify workflow receives the long-lived GitHub App private key — templates/.github/workflows/house-graphify.yml:31 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Smartcloud drops ACCESS_TOKEN fallback — .github/workflows/smartcloud.yml:69 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 32 functions depend on the 32 functions this change touches.

Health — grade A; no new coupling hotspots.

Verification — 32 functions in the blast radius were not formally verified this run (proofs are advisory here).

Health delta baseline: last indexed commit 9b4e74e, 2 commit(s) behind this PR's base.

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 32 function(s) in the blast radius were not formally verified this run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk: high. Left a non-blocking comment and did not approve: Cursor Security Agent still has an unresolved high finding on the smartcloud App token scope, and this authentication change is above the medium approval threshold. Human review is needed; no reviewers were assigned because the only CODEOWNER is the PR author.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant