Repository navigation
Read inbound texts the way the relay sends them, and only from the relay - #25
Merged
Merged
Conversation
…lay. The relay forwards Twilio's own form fields (From, Body), signed with x-relay-signature over the raw body. The webhook only read lowercase JSON, so a real text would have been rejected. It now reads the raw body in either shape and, once RELAY_INBOUND_SECRET is set, refuses anything not signed by the relay for this exact URL. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
The app half of texting in. It is safe to ship before the Coachatron number exists: with no secret and no relay route, nothing changes.
Why
/webhooks/smsread lowercase JSON (from,body). The relay forwards Twilio's own fields (From,To,Body, …) urlencoded, signed withx-relay-signature= base64 HMAC-SHA256(secret, url + raw body) (noctusoft-relay/inbound.js:258-273,inbound-secrets.js:44). So a real text would have hit "Missing from or body".What changes
/webhooks/smskeeps the raw body (express.raw, the same pattern as/webhooks/store). It reads the relay's form fields, plus the JSON shape that tests and local tools use.src/lib/inboundSms.tschecks the relay signature withtimingSafeEqual, overINBOUND_SMS_URLplus the raw body. The URL defaults to${APP_BASE_URL}/webhooks/smsand must equal the manifest URL exactly.RELAY_INBOUND_SECRETis set, an unsigned request, a wrong secret, or a signature for a different URL gets 401.One number for every environment
Sending: every environment texts from the one number.
Receiving: the relay routes a number to a single URL, so incoming texts point at uat while you test. At launch, one manifest line moves them to production. Full steps are in
docs/PLATFORM.md§3 and ROADMAP M14.Still to do outside this repo:
sms: { numbers: [...] }and pointinbound.smsat uat. Mint the secret withrelay-keys.js inbound-secret --product coachatron --rotate, then point the Twilio number at the relay.RELAY_INBOUND_SECRET,INBOUND_SMS_URL=https://uat.coachatron.com/webhooks/smsandCOACHATRON_SMS_NUMBER.Tests
test/inbound-sms.test.ts(3 tests):/start/link, with no model call.npm test121/121 on three consecutive runs. Typecheck, lint (0 errors) and build pass.🤖 Generated with Claude Code