Skip to content

Runs and email-test find the Resend key in the Credentials - #194

Merged
JacobStephens2 merged 4 commits into
mainfrom
issue-191
Sep 30, 2026
Merged

JacobStephens2 merged 4 commits into
mainfrom
issue-191

Conversation

@JacobStephens2

Copy link
Copy Markdown
Owner

Summary

A Run, a Spec run and email-test now find the Resend key in the Credentials when RESEND_API_KEY is unset or empty. This is how the key reaches a Run started from cron, nohup or an agent's shell.

 Resend::new / Setup's key hint
-  std::env::var("RESEND_API_KEY")
+  ResendKey::find()                     # src/resend_key.rs
+    RESEND_API_KEY, if set and non-empty   -> Source::Environment
+    else ~/.thirdshift/credentials.toml    -> Source::Credentials(path)
+      missing file        -> no key
+      bad TOML / unknown table or key / non-string key -> error naming the file and the key
+      readable by others  -> used, with "warning: ... chmod 600 <path>"
+    else -> the missing-key message from #190, with the real path
  • The first commit is the prefactor: all key lookups go through resend_key, and the existing tests pass unchanged. The second commit adds the file fallback.
  • Only email-test, Setup, and a Run that asks for a notification read the file. A Run with no notification, update, version and help never read it.
  • The README's Email and Run notifications sections, the help text and CONTEXT.md (the Credentials entry) describe the file and the lookup order.

Evidence

The new tests run through the binary. ResendStandIn's recorded Authorization header shows which key was sent:

email-test, RESEND_API_KEY unset/empty, credentials key re_file_456   -> Bearer re_file_456
email-test, both set                                                  -> Bearer re_test_123 (env)
run --email / email.always / spec run, key only in file               -> Bearer re_file_456
no key anywhere                           -> exit 1, before any work, "no Resend API key. Either: ..."
[resend] kye / bad TOML / key = 456       -> exit 1, before any work, names file + key, nothing sent
mode 640/604/644                          -> sent, "warning: ... chmod 600 <path>"
run without notification + broken file   -> exit 0
help / version with a broken file         -> exit 0, empty stderr
  • Before: on main, the new tests without_resend_api_key_it_sends_with_the_key_in_the_credentials, with_no_key_anywhere_it_lists_every_way_to_give_one, broken_credentials_stop_it_naming_the_file_and_the_key and credentials_others_can_read_are_used_with_a_warning fail.
    After: full cargo test passes, and cargo clippy --all-targets -- -D warnings is clean.

Merge Danger

Door: two-way

Nothing writes the Credentials yet (that's #192), and nothing else is persisted.

Blast Radius: email

Only a user who already has a hand-written ~/.thirdshift/credentials.toml sees a difference. If that file is broken, a Run that asks for a notification now stops before any work instead of complaining about RESEND_API_KEY. A Resend refusal now ends with ; the key came from <source>.

Unaddressed findings

Standards

  • ResendKey::find prints the permissions warning as a side effect. Skipped: the spec wants the warning: line wherever the file is read. Returning it as data would widen the interface just so every caller prints the same thing.
  • ResendKey exposes pub fields. Skipped: there are two plain values and two callers, so accessors would add interface without depth. The key.key chain it caused is fixed by the rename to secret.
  • The missing-key message says "run thirdshift setup", but Setup doesn't write the key yet. Skipped: the spec mandates this text word for word, and Setup asks for the Resend key and writes the Credentials #192 makes it true.
  • Setup's hint (src/questions.rs) still says "RESEND_API_KEY is unset or empty" and offers only export. Skipped: Setup asks for the Resend key and writes the Credentials #192 replaces that hint with the hidden key prompt.
  • parse repeats the shape of config.rs's strict parser. Skipped: the repeat is deliberate so both files are strict in the same way, and the reviewer also rated it acceptable.

Spec

  • A Resend refusal now names where the key came from (scope creep). Kept: when RESEND_API_KEY silently overrides the saved key, an "API key is invalid" reply needs this to be actionable. It is also the one use of the key's source that the prefactor asks the lookup to report. Resend's text is still quoted word for word.
  • The error for a missing address and/or key now ends with a line nothing sent instead of ; nothing sent. Kept: the missing-key message has several lines, so a ; suffix would hang off its last line.
  • help gained a Credentials paragraph (not asked for). Kept: the old help said the key comes only from RESEND_API_KEY, which is now false.
  • A broken Credentials file aborts thirdshift setup after email.from is answered, not before any questions. Skipped: moving that check to the start is Setup asks for the Resend key and writes the Credentials #192's acceptance criterion. Until then, the abort writes nothing and names the file and the key.

Closes #191

🤖 Generated with Claude Code

JacobStephens2 and others added 4 commits September 30, 2026 17:44
The email sender and Setup's key hint both ask resend_key, which reports
the key and where it came from, instead of each reading RESEND_API_KEY.
No behaviour changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With RESEND_API_KEY unset or empty, email-test and Runs that ask for a
notification read resend.key from ~/.thirdshift/credentials.toml, parsed
as strictly as the User config, warning when others can read it. With no
key anywhere, the error lists every way to give one. A refusal says where
the key came from.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Define the Credentials in CONTEXT.md, name the key's value secret so the
sender doesn't read key.key, build the Credentials' name in one place, and
cover broken Credentials in a Spec run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 merged commit b89a84e into main Sep 30, 2026
13 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-191 branch September 30, 2026 18:10
@JacobStephens2 JacobStephens2 mentioned this pull request Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Runs and email-test find the Resend key in the Credentials

1 participant