Repository navigation
Resolve keychain credentials on zero-persistence projects - #5257
Merged
Merged
Conversation
When a project doesn't save dataclips, the run's input is wiped as soon as the worker fetches it. That happens before the worker asks for any credential, so keychain resolution found nothing to match against and fell back to the default credential. The run channel now reads each keychain's external id from the input just before the wipe, and keeps the result in the socket's assigns. If those ids are missing on a zero-persistence run (for example a rejoin after the wipe), the channel returns an error instead of quietly handing over the default. Credential lookups for a run now also check ids against the run's snapshot, not the live workflow. The worker was given the ids in the snapshot, so those are the ones it can ask for, even if someone has edited the job since.
The changeset checks keychain paths with a loose regex, so paths Postgres can't parse, like `$..user_id`, can be saved. Evaluating every keychain in one query meant a single bad path raised in fetch:dataclip and failed the whole run on zero-persistence projects, including jobs that never touch a keychain. Each keychain's path is now evaluated in its own query. A Postgres error marks only that keychain, and the job that asks for it gets an error rather than the default credential, since a broken path is a config mistake, not missing input. Fetching a keychain credential on projects that save dataclips now evaluates only that keychain's path, and runs whose snapshot has no keychain jobs skip the query altogether.
The default-argument head made resolve_credential/3 accept a
%Credential{} with a selectors map, which only raised
FunctionClauseError, and its spec failed dialyzer. Credentials and runs
now share /2, and /3 takes a run only.
With that contract fixed, dialyzer flagged the run channel's fallback
error clause as unreachable. It isn't: resolve_credential_body/2 passes
through any other OAuth refresh failure, such as a timeout's error map,
or a changeset when the new token can't be saved. Its spec now says so.
midigofrank
marked this pull request as ready for review
October 22, 2026 09:25
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5257 +/- ##
=====================================
Coverage 91.4% 91.5%
=====================================
Files 465 465
Lines 23416 23453 +37
=====================================
+ Hits 21409 21450 +41
+ Misses 2007 2003 -4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
I have everything I need. All three checks resolve cleanly:
Security Review ✅
|
A second fetch:dataclip on a zero-persistence run recomputed the keychain selections from the already-wiped body. Every path then matched nothing, the good selections were overwritten, and the job quietly got the default credential. The channel now captures selections only on the first fetch, and the resolver treats a dataclip with wiped_at set as unavailable input, so a rejoined channel errors too.
stuartc
approved these changes
Oct 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
When a project doesn't save dataclips, the run's input is wiped as soon as the worker fetches it. That happens before the worker asks for any credential, so keychain resolution found nothing to match against and fell back to the default credential.
The run channel now reads each keychain's external id from the input just before the wipe, and keeps the result in the socket's assigns.
Each keychain's path is evaluated in its own query. The changeset only checks paths with a loose regex, so a path Postgres can't parse (like
$..user_id) can be saved.Credential lookups for a run now also check ids against the run's snapshot, not the live workflow. The worker was given the ids in the snapshot, so those are the ones it can ask for, even if someone has edited the job since.
Closes #5091
Validation steps
aliceand body{"alice_key": "x"}, one with External IDboband body{"bob_key": "x"}, and one with no External ID and body{"default_key": "x"}.$.user_idand the third credential as its default.fn(state => { console.log(Object.keys(state.configuration)); return state; }). (Keys aren't scrubbed from logs, values are.){"user_id": "alice"}to the webhook. The run log showsalice_key. Onmainit showsdefault_key. Then POST{"user_id": "bob"}and check forbob_key.$..user_id(the form accepts it) and add a second job off the trigger that doesn't use the keychain. POST again: the keychain job fails with "Could not select a keychain credential: its path could not be evaluated against the run's input", and the other job still runs.Additional notes for the reviewer
AI Usage
Please disclose whether you've used AI anywhere in this PR (it's cool, we just
want to know!):
You can read more details in our
Responsible AI Policy
Pre-submission checklist
/reviewwith Claude Code)
(e.g.,
:owner,:admin,:editor,:viewer)