diff --git a/CLAUDE.md b/CLAUDE.md index 5b19b87..a4aba83 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,7 +12,7 @@ - **Mongo driver:** `go.mongodb.org/mongo-driver/v2` - **HTTP client:** `net/http` (stdlib, no external HTTP client) - **JSON:** `encoding/json` (stdlib) -- **Crypto:** `crypto/rsa` + `crypto/sha256` (webhook sig: RSA-PKCS1v15+SHA-256); `crypto/ed25519` (X-GHL-Signature fallback, active July 2026+) +- **Crypto:** `crypto/ed25519` (webhook sig: Ed25519 for both `x-wh-signature` and `X-GHL-Signature`; key from GHL Marketplace app settings) - **Config:** Environment variables only (no config files) - **Deploy:** Docker + Docker Compose on Vultr @@ -64,8 +64,8 @@ GHL sends `ProviderOutboundMessage` to our delivery URL: "userId": "..." } ``` -Verified via `x-wh-signature` header using RSA-PKCS1v15 + SHA-256. -Future: `X-GHL-Signature` header using Ed25519 (GHL migration July 2026). +Verified via `x-wh-signature` header using Ed25519 (key from GHL Marketplace app settings). +`X-GHL-Signature` also Ed25519 (same key; GHL will activate this header July 2026). ## Project Structure @@ -149,7 +149,7 @@ Validated at startup. Missing required vars → log error + os.Exit(1). ## Key Implementation Notes -1. **Webhook signature verification is mandatory** — GHL sends `x-wh-signature` on every webhook. Verify with RSA-PKCS1v15 + SHA-256 using the static RSA public key from GHL docs (set in `GHL_WEBHOOK_PUBLIC_KEY` env var). From July 2026, also support `X-GHL-Signature` (Ed25519). The handler checks `X-GHL-Signature` first and falls back to `x-wh-signature`. +1. **Webhook signature verification is mandatory** — GHL sends `x-wh-signature` on every webhook. Verify with Ed25519 using the key from your GHL Marketplace app settings (set in `GHL_WEBHOOK_PUBLIC_KEY` env var). From July 2026, GHL will also send `X-GHL-Signature` (same Ed25519 scheme). The handler checks `X-GHL-Signature` first and falls back to `x-wh-signature`. 2. **OAuth tokens are per-location** — store `locationId` → `{ access_token, refresh_token, expires_at }` in MongoDB. Refresh before expiry. 3. **Phone normalization is critical** — GHL sends E.164 (`+639XXXXXXXXX`), Cast expects `09XXXXXXXXX`. Get this wrong = messages fail. 4. **Status updates must use the provider's token** — only the conversation provider marketplace app tokens can update message status.