Widen a source's scope
You have granted a credential access to more than it had — more repositories, more Jira projects — and you want Prism to hold all of it.
The one thing to know before you start: widening a grant is invisible to Prism. Nothing fails, nothing turns red, no log line marks the change, and the self test stays green throughout. The newly-reachable repositories or projects simply start appearing in the sweeps that happen after the change. Their history does not arrive at all until the days it falls in are walked again.
Prism will point you at this page, and it is worth knowing why it has to
watch for this specially. The two verdicts that normally mark an incomplete
day both describe rows the walk knew it missed — capped_days a day that hit
the per-query ceiling, unenriched_days a day whose enrichment could not be
reached. A day walked under a narrower grant is not incomplete by either
measure: every repository outside that grant is a row the walk never knew
existed, so both verdicts are correctly silent and the day reads as covered.
So Prism watches the credential's reach directly instead. When it can see more
than it could on the previous run — or less — Ingestion activity carries a
credential reach line naming the days that were walked under the old grant,
and the exact ingest.github.rewalkFrom value to paste to fill them. The
notice narrows as the re-sweep covers the span and clears when it has covered
the whole of it. Prism never starts that sweep on its own; the decision is
yours, for the reasons under If the new scope is much larger than the old
one.
When there is nothing to disclose, the same line says what Prism does know rather than going blank, and the three quiet cases are different sentences:
- checked … and unchanged — the last completed run compared the credential's reach against the previous run's and found no movement beyond ordinary repository churn. The count and when it was checked are on the line. This is the reassurance; nothing needs doing.
- measured once … and not yet compared — one run has taken a reading and there is nothing to compare it against yet. A fresh install, or the first run after the upgrade that added the probe. The next completed run is the first that can say whether the reach moved.
- not yet measured — no completed run has recorded a reading. An ingest
image older than the probe, or a probe that has not yet succeeded. Until one
does, a change in the credential's reach would go unreported, which is why
this one is coloured as a gap. The line names which image is the old one:
on the shipped default (
ingest.engine.mode: authoritative) GitHub is walked by the manifest engine, which measures reach since 0.14.0, so it reads not yet measured on this lane and names that version; on the legacy lane (ingest.github.engineMode: "off") the probe dates from 0.13.0.
A source that has no credential reach to measure — Jira, for instance — shows
no credential reach line at all. That absence is deliberate: it means "there
is nothing to say", never "checked and unchanged".
Both lanes measure since 0.14.0. Until then the probe ran on the legacy
GitHub lane only (since 0.13.0), and with ingest.engine.mode at its default
of authoritative — the manifest engine walking GitHub — nothing measured.
From 0.14.0 the engine makes the same one call per organisation before each
walk
(GET /search/repositories?q=org:<org>&per_page=1, reading only the count),
compares it against the baseline the previous completed run stored, and records
the same notice and the same check stamp, so a default install reads
checked … and unchanged after its second completed run like any other. If
you held GitHub on the legacy lane to keep the measurement
(ingest.github.engineMode: "off"), that is no longer needed for this
reason; flipping back is
Flipping the engine, and the engine's first run compares
against the baseline the legacy lane left — same token, same reach, nothing
reported. The probe runs for every source whose recipe is GitHub: the shipped
github stream and any instance you registered from that recipe under a name
of your own, which shows its own credential reach line.
What it will not catch, so you know when this page is still the only thing watching:
- a repository or two created in your organisation, which is below the threshold on purpose — otherwise every new repository would claim your history was incomplete and offer you an expensive remedy for it;
- a grant widened in many small steps, where no single run's change is big
enough to report. The reach it measured is recorded on every run whatever
the threshold did, and appears in the self test bundle as
credential_reach_count, so comparing two bundles a month apart shows a drift that no single run announced.
If you have just widened a grant and want to be certain rather than wait for the next run to notice, the rest of this page is the procedure.
This applies to a credential rotation as much as to a deliberate widening. Replacing a token with one that can see more — a fine-grained token re-scoped from a hand-picked list to All repositories is the common case — widens the grant whether or not you thought of it that way. If you have just done that, you are in the right place; Rotate a credential covers the swap itself and sends you here for the history.
So a widened grant lands as a step in your data rather than as a fill: the mirror holds the old, narrower scope up to the day you widened it and the new, wider scope afterwards, with nothing separating the two. Asked about activity over a window that straddles that date, Prism answers honestly over what it holds — and what it holds jumped, so the answer shows a rise that is an artefact of the grant rather than anything your engineers did.
Making that step go away is one helm upgrade, and it is the whole point of
this page.
1. Make the grant and the chart agree
Which of these you have to do depends on the source, because the two sources scope themselves differently.
| Source | What decides the scope | What to change in the chart |
|---|---|---|
| GitHub | The credential, and only the credential. The repositories a token or App installation can reach are the repositories Prism ingests | Nothing for the mirror — ingest.github.orgs names organisations, not repositories. If you have set mcps.githubLive.allowedRepos, add the new repositories to it: that list bounds the live drill-down lane and is not widened by the grant |
| Jira | The credential and ingest.jira.projects, and the narrower of the two wins | If you have set ingest.jira.projects, add the new keys to it. If it is empty — meaning every project the credential can see — leave it alone |
| A source Prism queries in place | The credential on the source | Nothing, and nothing below applies: there is no mirror, so the next question already sees the wider scope |
The Jira row is the one that catches people. ingest.jira.projects is a filter
applied on top of what the credential can see, so granting a service account
another eighty projects while that value still lists sixteen changes nothing at
all — no error, no warning, and sixteen projects' worth of data. If you set that
value at go-live and have forgotten whether you did, look before you widen the
grant rather than after:
helm get values <release> -n <namespace> | grep -A2 'jira:'
Router spend has no per-repository or per-project grant to widen; it reports what its management credential can see.
2. Prove the credential reaches the new ground
Before re-sweeping anything, check that the grant is what you think it is. The commands are the ones in Verifying by hand — run them with the widened credential, and compare the counts against what you got before.
For GitHub the assertion that matters is the search, not the token's own metadata:
curl -sS -H "Authorization: Bearer <token>" \
"<your GitHub API URL>/search/issues?q=org:<org>+type:pr&per_page=1"
total_count should have risen by roughly the traffic of the repositories you
just added. If it has not moved, the grant did not land — a fine-grained token
awaiting an organisation owner's approval is the usual reason, and it fails this
way rather than by being refused.
For Jira, list the projects the credential can actually see and compare that
against the keys you expect and, if you set it, against
ingest.jira.projects:
curl -sS -H "Authorization: Bearer <PAT>" \
"<your Jira base URL>/rest/api/2/project" # .[].key
When the credential cannot see the whole organisation
Check the token's repository access before you change anything else. A fine-grained personal access token is scoped one of two ways, and the difference is the usual reason a mirror is narrower than the organisation:
- All repositories — every repository the resource owner has, including ones created later. This is what an organisation-wide mirror needs, and it is what What the GitHub credential needs asks for.
- Only select repositories — the ones somebody picked, and no more. A token scoped this way is the commonest cause of plausible-but-low figures, and widening it is a change to the token, not to Prism.
A correction, if you read this page before September 2026. It said a fine-grained token "stops at 50 repositories" and that a wider scope therefore "has to become a GitHub App installation". That was wrong, and it was wrong in a way that sent people to a bigger change than they needed. The count applies to picking repositories one at a time under Only select repositories; All repositories carries no such cap. If your token is narrow, re-scoping it is the first thing to try.
A GitHub App installation is still the better credential for an organisation of any size, and worth moving to deliberately rather than as an emergency:
- the grant is the scope. An installation token is not tied to a person, so what Prism can see is what the App was granted — it does not move when somebody's team membership does, and it does not quietly widen because the token's owner can see more than the grant;
- no user seat, and it is auditable and revocable on its own;
- higher rate limits, which matter once the walk is large;
- short-lived tokens, minted here and re-minted before expiry.
The rest of this section is how to configure one.
This is not a token swap, and an earlier version of this page said it was. It told you Prism accepted an installation token wherever it accepted a PAT and that the change needed nothing beyond the secret value. The header is indeed the same — which is exactly why the mistake was easy to make and hard to see. A GitHub App installation access token expires one hour after it is minted. Pasting one into
secrets.githubTokengives you a backfill that runs, a mirror that fills and a self test that goes green, followed within the hour by a source that fails withbad_credential— which is what a revoked token looks like, so the diagnosis starts in the wrong place.Prism now mints its own installation tokens and re-mints them before they expire, which is the only arrangement that survives a backfill. That means giving it the App's identity and key, below, and not a token.
Three values, and they go in together:
| What | Where | Where you find it |
|---|---|---|
| App ID | ingest.github.app.appId | the App's settings page, "App ID" |
| Installation ID | ingest.github.app.installationId | install the App on the organisation, then read the number at the end of the Configure URL: .../installations/<this> |
| Private key (PEM) | secrets.githubAppPrivateKey | the App's settings page → Generate a private key. GitHub downloads a .pem once and never shows it again |
helm upgrade <release> <chart> -n <namespace> -f <your values file> \
--set ingest.github.app.appId=123456 \
--set ingest.github.app.installationId=98765432 \
--set-file secrets.githubAppPrivateKey=./your-app.private-key.pem
--set-file rather than --set: a PEM is multi-line and --set flattens it.
(If your secret store hands the key back on one line with literal \n between
the lines, Prism reads that too — but prefer the file.)
Five things worth knowing before you run it:
- All three together, or none. A half-configured App is refused at the start
of the run rather than quietly falling back to
secrets.githubToken. The fallback would ingest under a credential you did not choose and record nothing about having done so. - The App wins over
secrets.githubToken. You do not have to empty the old key in the same breath, and leaving it there is safe. The ingest names the credential it is using on its second log line, so there is no guessing which is in force. - The permissions are the same list as the fine-grained token's — what the GitHub credential needs. An App is a different kind of credential, not a different grant.
- The private key is the credential, and it is a stronger one than the token it replaces. An installation token is scoped to one installation and lives an hour; the key mints tokens for every installation of that App, for as long as it exists. So install the App on the organisations you are measuring and no others — its reach is the key's reach — keep the key wherever the rest of your secrets live, and see Rotating credentials for replacing it without an outage. Prism never logs it, never sends it to GitHub and never renders it in an error.
- The optional live drill-down MCP still needs a PAT.
mcps.githubLivewraps GitHub's own server, which reads a static token; it cannot mint. If you have it on, keepsecrets.githubTokenpopulated for it. The ingest — the half that needs the wide scope — uses the App.
Then check what the installation actually reaches, which is the number the whole exercise is for:
kubectl logs -n <namespace> job/<release>-ingest-check-github
github check: PASS {"auth": {"credential": "github_app", "app_id": "123456",
"repository_selection": "all", "repositories": 3412}, ...}
repository_selection: selected means somebody chose a subset when installing
the App, and repositories is how many — that is a cap of your own making
rather than GitHub's, and it is changed on the App's installation page, not
here.
3. Re-sweep, so the history arrives too
From here on the widened grant fills forward on its own. To pull in the past of the newly-reachable repositories or projects, name the date you want the source to reach back to:
helm upgrade <release> <chart> -n <namespace> -f <your values file> \
--set ingest.github.rewalkFrom=2026-03-01
and, separately, for Jira:
--set ingest.jira.rewalkFrom=2026-03-01
This is the same lever as widening the window later, used for a different reason: there you are reaching further back than the mirror goes, here you are re-walking ground the mirror already covered because it was walked with a narrower credential. The mechanics are identical and worth reading there in full. The parts that matter most here:
- nothing is truncated. The re-sweep upserts, so the source keeps answering throughout and the repositories you already had are not disturbed;
- it costs what a backfill of that length costs, and a much wider scope makes it more expensive than the original backfill was. See below;
- it happens once per date. Leave the value in your values file; to re-sweep again, name a different date;
- pick a date at or before the source's current horizon, or you will fill the recent months and leave a hole behind them.
4. Prove it landed
Two questions, and they are different: is the new ground in the mirror, and is it in there whole?
Which repositories the mirror now holds, against which it held before:
kubectl exec -n <namespace> statefulset/<release>-postgres -- \
psql -U postgres -d prism -c "
SELECT repo, count(*) AS prs, min(created_at)::date AS oldest
FROM pull_requests GROUP BY repo ORDER BY prs DESC;"
A newly-granted repository whose oldest is the day you widened the grant has
been picked up but not re-swept — the re-sweep either has not run yet, did not
reach back far enough, or was killed part way.
Whether GitHub could enrich what it fetched. Repository access is granted in two halves that can disagree: Prism can find a pull request through search and still be refused the call that carries its size, its reviews and its issue links. Every such pull request is counted, per repository:
kubectl exec -n <namespace> statefulset/<release>-postgres -- \
psql -U postgres -d prism -c "
SELECT detail->'enrichment_shortfall' FROM ingest_state WHERE source = 'github';"
unenriched against prs is the size of the hole, and repos names the worst
offenders. A repository sitting at 100% unenriched is a permission boundary —
that repository is searchable but not readable by this credential — while a
shortfall smeared thinly across every repository is not about permissions at
all. Prism labels the affected figures either way: pull-request sizes, time to
first review, time in review and reviews given are the five that rest on the
enriched half, and each says so when it is answering over an incomplete one.
Finally, confirm the coverage horizon moved if you expected it to, per widening the window later. The horizon moves only when a re-sweep finishes; if it has not moved, name a nearby date and run it again.
If the new scope is much larger than the old one
Going from a handful of repositories to an entire organisation is not the same operation as adding a few, and it is worth sizing before you start rather than discovering it during the sweep.
- Time. Backfill time is very nearly proportional to the traffic being
walked and inversely proportional to
ingest.github.maxRequestsPerMinute. A re-sweep over a scope ten times wider takes about ten times as long at the same request cap; raise the cap only as far as your platform team says the instance will take. - Load on your instance. A busy day inside a very large organisation costs many more calls than a quiet one, because the walk splits any day whose activity exceeds what one search can answer and re-asks it in halves. This is what keeps the row counts complete, and it is paid for in requests. See how fast Prism calls your GitHub and Jira.
- Disk. The mirror grows with the scope. Check the volume has room before a large re-sweep rather than after — see disk.
- Do not shorten
ingest.runDeadlineSecondsto make it retry sooner. On a much wider scope each day takes longer, and a run killed before it finishes a day records nothing. Tighteningest.github.scheduleinstead. - The answers change meaning, not just size. Once the scope is an entire organisation, an org-wide figure is about the whole organisation — which may be far more than the teams whose question it is. Narrowing at question time, by team or by person, is the supported way to get back to the smaller number.
Related
- Rotate a credential — the same operation in reverse is a common accident: a replacement token narrower than the one it replaces loses repositories with no error at all.
- Take a source live — the go-live procedure, including what each credential needs and how to verify one by hand.
- Known failure modes — in particular, figures that are plausible but low.