fix: resend email send path - http_post_auth was dropping silently

The wrapper now logs the response and returns a structured ok/error
shape. Four call sites converge on a single send_email helper.
Resend deliveries verified end to end against
will.anderson@neurontechnologies.ai (delivery IDs 492fa066, 74258223,
69a3d9ab, f6d1c889).

Root cause: http_post_auth in dist/web_stubs.c only set the
Authorization: Bearer header. Resend rejects requests without
Content-Type: application/json with HTTP 422 missing_required_field
because it parses the body as form-urlencoded. The 422 response was
being captured by the El handler but not parsed, so callers logged
the error body and returned ok-200 to the client. Two endpoints also
built malformed JSON by interpolating the raw request body unquoted
into the text field.

Fix:
- Added http_post_auth_json (Bearer + Content-Type: application/json)
  alongside http_post_auth in dist/web_stubs.c. Stripe form-POST
  callers stay on http_post_auth, JSON callers (Resend now, others
  later) move to the json variant.
- New send_email(from_addr, to, subject, html, text) wrapper in
  src/main.el. JSON-escapes all user-provided fields, parses the
  Resend response into a structured ok/error envelope, and println's
  the outcome ([email] sent id=<id>) for Cloud Run log surfaces.
- Refactored four call sites onto the wrapper: /api/enterprise-inquiry,
  /api/developer-interest, /api/waitlist, /api/attest, the family
  invite branch in /api/family/invite, and both DocuSeal completion
  branches in /api/docuseal/webhook/<token>.
- Untracked dist/ source files (web_stubs.c, vessel_stubs.c,
  soul-demo.c, entrypoint.sh, engram-snapshot.json) are now committed
  - generated artifacts (main.c, binaries) stay ignored. Without this
  the next CI rebuild would regress the fix.
This commit is contained in:
Will Anderson
2026-05-02 12:37:54 -05:00
parent 79cd461b83
commit 42f8602457
8 changed files with 2454 additions and 66 deletions
+15 -1
View File
@@ -1,5 +1,8 @@
.env
dist/
# Build output dir. Hand-written sources under dist/ are explicitly
# un-ignored at the bottom of this file - those must travel with the
# repo so future builds can re-produce a working image.
dist/*
.el/
src/*.elc
src/*.map.json
@@ -7,3 +10,14 @@ src/index.html
src/about.html
src/terms.html
src/enterprise-terms.html
# Track hand-written source under dist/ that is NOT generated by elc.
# These are the C stub shims and entry scripts the Dockerfile.stage COPYs
# into the image; without these the build cannot produce a working
# binary. Generated artifacts (main.c, main-combined.el, binaries) stay
# ignored.
!dist/web_stubs.c
!dist/vessel_stubs.c
!dist/soul-demo.c
!dist/entrypoint.sh
!dist/engram-snapshot.json
+1 -1
View File
@@ -84,7 +84,7 @@ else
SED_INPLACE=(-i '')
fi
sed "${SED_INPLACE[@]}" \
's|#include "el_runtime.h"|#include "el_runtime.h"\nel_val_t http_get_auth(el_val_t url, el_val_t tok);\nel_val_t http_post_auth(el_val_t url, el_val_t tok, el_val_t body);\nel_val_t http_delete_auth(el_val_t url, el_val_t bearer_tok, el_val_t apikey);\nel_val_t cwd(void);\nel_val_t color_bold(el_val_t s);\nel_val_t unix_timestamp(void);\nel_val_t gcs_write(el_val_t bucket, el_val_t object_name, el_val_t content);\nel_val_t gcs_read(el_val_t bucket, el_val_t object_name);\nel_val_t supabase_insert(el_val_t project_url, el_val_t service_key, el_val_t table, el_val_t row_json);\nel_val_t supabase_get(el_val_t project_url, el_val_t service_key, el_val_t table_and_query);\nel_val_t supabase_auth_user(el_val_t project_url, el_val_t anon_key, el_val_t user_jwt);\nel_val_t supabase_admin_invite(el_val_t project_url, el_val_t service_key, el_val_t body_json);\nel_val_t supabase_upsert_user(el_val_t project_url, el_val_t anon_key, el_val_t user_jwt, el_val_t table_and_query, el_val_t row_json);|' \
's|#include "el_runtime.h"|#include "el_runtime.h"\nel_val_t http_get_auth(el_val_t url, el_val_t tok);\nel_val_t http_post_auth(el_val_t url, el_val_t tok, el_val_t body);\nel_val_t http_post_auth_json(el_val_t url, el_val_t tok, el_val_t body);\nel_val_t http_delete_auth(el_val_t url, el_val_t bearer_tok, el_val_t apikey);\nel_val_t cwd(void);\nel_val_t color_bold(el_val_t s);\nel_val_t unix_timestamp(void);\nel_val_t gcs_write(el_val_t bucket, el_val_t object_name, el_val_t content);\nel_val_t gcs_read(el_val_t bucket, el_val_t object_name);\nel_val_t supabase_insert(el_val_t project_url, el_val_t service_key, el_val_t table, el_val_t row_json);\nel_val_t supabase_get(el_val_t project_url, el_val_t service_key, el_val_t table_and_query);\nel_val_t supabase_auth_user(el_val_t project_url, el_val_t anon_key, el_val_t user_jwt);\nel_val_t supabase_admin_invite(el_val_t project_url, el_val_t service_key, el_val_t body_json);\nel_val_t supabase_upsert_user(el_val_t project_url, el_val_t anon_key, el_val_t user_jwt, el_val_t table_and_query, el_val_t row_json);|' \
dist/main.c
echo "==> Building Docker image marketing:${TAG} for linux/amd64"
+1
View File
File diff suppressed because one or more lines are too long
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# entrypoint.sh — Start soul-demo then neuron-web in the same container.
#
# soul-demo runs in the background on :7772 (localhost only, not exposed).
# neuron-web runs in the foreground on :8080 (Cloud Run health checks this).
# If neuron-web exits, the container exits. Soul crashing is non-fatal —
# chat will return "demo soul not responding" but the page stays up.
set -euo pipefail
echo "[entrypoint] starting soul-demo on :7772"
/usr/local/bin/soul-demo &
SOUL_PID=$!
# Give the soul a few seconds to load its engram and seed safety nodes
sleep 4
echo "[entrypoint] soul-demo started (pid=$SOUL_PID)"
echo "[entrypoint] starting neuron-web on :${PORT:-8080}"
exec /usr/local/bin/neuron-web
+1898
View File
File diff suppressed because one or more lines are too long
+34
View File
@@ -0,0 +1,34 @@
#include <stdint.h>
#include "el_runtime.h"
/* Vessel stub implementations — graceful no-ops for embodiment functions
* not linked into the soul binary. */
el_val_t avatar_speak(el_val_t text) { return EL_STR("{\"error\":\"avatar vessel not in soul binary\"}"); }
el_val_t avatar_speak_stream(el_val_t text) { return EL_STR("{\"error\":\"avatar vessel not in soul binary\"}"); }
el_val_t avatar_stream_speak(el_val_t sid, el_val_t text) { return EL_STR("{\"error\":\"avatar vessel not in soul binary\"}"); }
el_val_t avatar_stream_close(el_val_t sid) { return 0; }
el_val_t did_post_stream_sdp(el_val_t stream_id, el_val_t body) { return EL_STR("{\"error\":\"did vessel not in soul binary\"}"); }
el_val_t voice_speak(el_val_t text) { return EL_STR("{\"error\":\"voice vessel not in soul binary\"}"); }
el_val_t voice_speak_with_voice(el_val_t text, el_val_t voice_id) { return EL_STR("{\"error\":\"voice vessel not in soul binary\"}"); }
el_val_t voices_list(void) { return EL_STR("{\"error\":\"voice vessel not in soul binary\"}"); }
el_val_t camera_frame(el_val_t sid) { return EL_STR(""); }
el_val_t camera_start(el_val_t device) { return EL_STR(""); }
el_val_t camera_stop(el_val_t sid) { return 0; }
el_val_t camera_faces(el_val_t sid) { return EL_STR(""); }
el_val_t mic_start(el_val_t device) { return EL_STR(""); }
el_val_t mic_stop(el_val_t sid) { return EL_STR(""); }
el_val_t mic_segment(el_val_t sid) { return EL_STR(""); }
el_val_t stt_transcribe(el_val_t audio_b64) { return EL_STR(""); }
el_val_t jfield(el_val_t key, el_val_t value) {
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("\""), key), EL_STR("\":\"")), value), EL_STR("\""));
}
el_val_t jfield_raw(el_val_t key, el_val_t value) {
return el_str_concat(el_str_concat(el_str_concat(EL_STR("\""), key), EL_STR("\":")), value);
}
el_val_t screen_capture(void) { return EL_STR(""); }
el_val_t mouse_click(el_val_t x, el_val_t y, el_val_t button) { return 0; }
el_val_t mouse_move(el_val_t x, el_val_t y) { return 0; }
el_val_t keyboard_type(el_val_t text) { return 0; }
el_val_t keyboard_keypress(el_val_t key) { return 0; }
el_val_t browser_navigate(el_val_t url) { return 0; }
el_val_t browser_eval(el_val_t url, el_val_t js) { return EL_STR(""); }
el_val_t browser_page(void) { return EL_STR("{\"error\":\"browser vessel not in soul binary\"}"); }
+326
View File
@@ -0,0 +1,326 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "el_runtime.h"
el_val_t http_get_auth(el_val_t url, el_val_t tok) {
char bearer[2048]; snprintf(bearer, sizeof(bearer), "Bearer %s", EL_CSTR(tok));
el_val_t hdr_val = EL_STR(bearer);
el_val_t headers = el_map_new(1, "Authorization", hdr_val);
return http_get_with_headers(url, headers);
}
/* supabase_get — GET from Supabase REST API with both apikey and Bearer headers */
el_val_t supabase_get(el_val_t project_url, el_val_t service_key, el_val_t table_and_query) {
char bearer[2048]; snprintf(bearer, sizeof(bearer), "Bearer %s", EL_CSTR(service_key));
char url[2048]; snprintf(url, sizeof(url), "%s/rest/v1/%s", EL_CSTR(project_url), EL_CSTR(table_and_query));
el_val_t headers = el_map_new(3,
"Authorization", EL_STR(bearer),
"apikey", service_key,
"Accept", EL_STR("application/json"));
return http_get_with_headers(EL_STR(url), headers);
}
el_val_t http_post_auth(el_val_t url, el_val_t tok, el_val_t body) {
/* Bearer-only. No Content-Type — Stripe's REST API consumes
* application/x-www-form-urlencoded bodies and is the heaviest
* caller of this helper. JSON callers (Resend, Anthropic, etc.) must
* use http_post_auth_json below, which sets Content-Type properly.
* Mixing Stripe form-POST and Resend JSON-POST through one helper was
* the root cause of the silent-drop bug — Resend 422s with "Missing
* `to` field" if the body arrives without Content-Type:
* application/json (it parses as form-encoded otherwise). */
char bearer[2048]; snprintf(bearer, sizeof(bearer), "Bearer %s", EL_CSTR(tok));
el_val_t hdr_val = EL_STR(bearer);
el_val_t headers = el_map_new(1, "Authorization", hdr_val);
return http_post_with_headers(url, body, headers);
}
/* http_post_auth_json — Bearer + Content-Type: application/json.
* Use for any REST API that consumes JSON request bodies (Resend,
* Anthropic, OpenAI, etc.). Returns the response body, or "" on
* transport failure. HTTP 4xx/5xx are NOT swallowed — the body is
* still returned so callers can parse error JSON. */
el_val_t http_post_auth_json(el_val_t url, el_val_t tok, el_val_t body) {
char bearer[2048]; snprintf(bearer, sizeof(bearer), "Bearer %s", EL_CSTR(tok));
el_val_t headers = el_map_new(2,
"Authorization", EL_STR(bearer),
"Content-Type", EL_STR("application/json"));
return http_post_with_headers(url, body, headers);
}
/*
* http_delete_auth - DELETE with Bearer auth + apikey (Supabase-style).
* The runtime exposes http_delete(url) but no header variant, so we
* implement DELETE-with-headers via http_post_with_headers using a
* libcurl-backed helper; falling back to http_delete if no auth available.
*
* For Supabase REST DELETE we need both Authorization (user JWT) AND
* apikey (anon key) - the apikey is required to reach PostgREST at all,
* the Bearer JWT is what auth.uid() checks for the RLS policy. The
* runtime's http_post_with_headers takes a method-agnostic body; we
* use libcurl directly via a tiny shim.
*/
#include <curl/curl.h>
typedef struct { char *buf; size_t len; size_t cap; } _stub_resp_t;
static size_t _stub_write(void *ptr, size_t size, size_t nmemb, void *u) {
_stub_resp_t *r = (_stub_resp_t*)u;
size_t add = size * nmemb;
if (r->len + add + 1 > r->cap) {
size_t nc = (r->cap ? r->cap : 4096);
while (nc < r->len + add + 1) nc *= 2;
char *nb = realloc(r->buf, nc);
if (!nb) return 0;
r->buf = nb; r->cap = nc;
}
memcpy(r->buf + r->len, ptr, add);
r->len += add;
r->buf[r->len] = '\0';
return add;
}
el_val_t http_delete_auth(el_val_t url, el_val_t bearer_tok, el_val_t apikey) {
CURL *c = curl_easy_init();
if (!c) return EL_STR("");
char auth_hdr[2048]; snprintf(auth_hdr, sizeof(auth_hdr), "Authorization: Bearer %s", EL_CSTR(bearer_tok));
char api_hdr[2048]; snprintf(api_hdr, sizeof(api_hdr), "apikey: %s", EL_CSTR(apikey));
struct curl_slist *hdrs = NULL;
hdrs = curl_slist_append(hdrs, auth_hdr);
hdrs = curl_slist_append(hdrs, api_hdr);
hdrs = curl_slist_append(hdrs, "Accept: application/json");
_stub_resp_t r = {0};
curl_easy_setopt(c, CURLOPT_URL, EL_CSTR(url));
curl_easy_setopt(c, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_easy_setopt(c, CURLOPT_HTTPHEADER, hdrs);
curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(c, CURLOPT_TIMEOUT, 60L);
curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, _stub_write);
curl_easy_setopt(c, CURLOPT_WRITEDATA, &r);
CURLcode rc = curl_easy_perform(c);
long http_code = 0;
curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &http_code);
curl_easy_cleanup(c);
curl_slist_free_all(hdrs);
if (rc != CURLE_OK) {
free(r.buf);
return EL_STR("");
}
if (http_code >= 400) {
free(r.buf);
return EL_STR("");
}
if (!r.buf) return EL_STR("");
/* Note: small leak by design - response lives until process exits.
* Mirrors the existing pattern in supabase_auth_user / gcs_get_token. */
return EL_STR(r.buf);
}
/*
* supabase_upsert_user - POST a row to a Supabase table on behalf of a
* logged-in user. Sends Authorization: Bearer <user-jwt> + apikey: <anon>
* + Prefer: resolution=merge-duplicates so RLS treats this as the user's
* own write and PostgREST upserts on the (composite) primary key.
*
* `table_and_query` is the path after /rest/v1/, e.g.
* "share_votes?on_conflict=share_id,user_id"
*
* Returns the response body (often "" with Prefer=return=minimal) or "" on
* non-2xx / network failure. Callers should re-fetch aggregate state via a
* separate GET to confirm the write landed.
*/
el_val_t supabase_upsert_user(el_val_t project_url, el_val_t anon_key,
el_val_t user_jwt, el_val_t table_and_query,
el_val_t row_json) {
char url[1024];
snprintf(url, sizeof(url), "%s/rest/v1/%s", EL_CSTR(project_url), EL_CSTR(table_and_query));
char bearer[2048];
snprintf(bearer, sizeof(bearer), "Bearer %s", EL_CSTR(user_jwt));
el_val_t headers = el_map_new(5,
"Authorization", EL_STR(bearer),
"apikey", anon_key,
"Content-Type", EL_STR("application/json"),
"Prefer", EL_STR("return=minimal,resolution=merge-duplicates"),
"Accept", EL_STR("application/json"));
return http_post_with_headers(EL_STR(url), row_json, headers);
}
/*
* supabase_auth_user — verify a Supabase user JWT and return the user object.
* Calls GET {project_url}/auth/v1/user with both apikey and Authorization headers.
* Returns the raw user JSON, or "" on failure.
*/
el_val_t supabase_auth_user(el_val_t project_url, el_val_t anon_key, el_val_t user_jwt) {
char bearer[2048];
snprintf(bearer, sizeof(bearer), "Bearer %s", EL_CSTR(user_jwt));
char url[512];
snprintf(url, sizeof(url), "%s/auth/v1/user", EL_CSTR(project_url));
el_val_t headers = el_map_new(3,
"Authorization", EL_STR(bearer),
"apikey", anon_key,
"Accept", EL_STR("application/json"));
el_val_t resp = http_get_with_headers(EL_STR(url), headers);
if (!resp) return EL_STR("");
return resp;
}
el_val_t cwd(void) {
static char buf[4096];
if (!getcwd(buf, sizeof(buf))) buf[0] = '\0';
return EL_STR(buf);
}
el_val_t color_bold(el_val_t s) { return s; }
el_val_t unix_timestamp(void) { return (el_val_t)(uintptr_t)(int64_t)time(NULL); }
/*
* supabase_insert — POST a JSON row to a Supabase table via the REST API.
* Requires both apikey and Authorization: Bearer headers (Supabase convention).
* Returns the response body, or "" on failure.
*/
el_val_t supabase_insert(el_val_t project_url, el_val_t service_key, el_val_t table, el_val_t row_json) {
char url[512];
snprintf(url, sizeof(url), "%s/rest/v1/%s", EL_CSTR(project_url), EL_CSTR(table));
char bearer[2048];
snprintf(bearer, sizeof(bearer), "Bearer %s", EL_CSTR(service_key));
el_val_t headers = el_map_new(4,
"Authorization", EL_STR(bearer),
"apikey", service_key,
"Content-Type", EL_STR("application/json"),
"Prefer", EL_STR("return=minimal,resolution=merge-duplicates"));
return http_post_with_headers(EL_STR(url), row_json, headers);
}
/*
* supabase_admin_invite — POST {project_url}/auth/v1/invite with the
* service-role key to create-or-find a user and email them a magic-link
* invitation. body_json should look like:
* {"email":"...","data":{"name":"..."},"redirect_to":"..."}
* Returns the JSON response (contains the new/existing user's id under "id").
*
* Used by the Stripe webhook to auto-provision a Supabase account whenever
* payment_intent.succeeded or setup_intent.succeeded fires - so every buyer
* lands on /account with an account waiting for them, regardless of whether
* they signed in before payment.
*/
el_val_t supabase_admin_invite(el_val_t project_url, el_val_t service_key, el_val_t body_json) {
char url[512];
snprintf(url, sizeof(url), "%s/auth/v1/invite", EL_CSTR(project_url));
char bearer[2048];
snprintf(bearer, sizeof(bearer), "Bearer %s", EL_CSTR(service_key));
el_val_t headers = el_map_new(3,
"Authorization", EL_STR(bearer),
"apikey", service_key,
"Content-Type", EL_STR("application/json"));
return http_post_with_headers(EL_STR(url), body_json, headers);
}
/*
* gcs_get_token — fetch an OAuth2 bearer token.
*
* Priority:
* 1. GOOGLE_ACCESS_TOKEN env var (local dev: `gcloud auth print-access-token`)
* 2. GCP metadata server (Cloud Run, automatic SA credentials)
*
* Returns the raw token string, or "" on failure.
*/
static el_val_t gcs_get_token(void) {
/* 1. Env override (local dev) */
const char *env_tok = getenv("GOOGLE_ACCESS_TOKEN");
if (env_tok && *env_tok) return EL_STR(env_tok);
/* 2. Metadata server */
el_val_t meta_url = EL_STR(
"http://metadata.google.internal/computeMetadata/v1/instance"
"/service-accounts/default/token");
el_val_t meta_headers = el_map_new(1, "Metadata-Flavor", EL_STR("Google"));
el_val_t resp = http_get_with_headers(meta_url, meta_headers);
if (!resp) return EL_STR("");
/* Parse access_token field from JSON response */
const char *s = EL_CSTR(resp);
const char *key = "\"access_token\":\"";
const char *p = strstr(s, key);
if (!p) return EL_STR("");
p += strlen(key);
const char *end = strchr(p, '"');
if (!end) return EL_STR("");
size_t len = (size_t)(end - p);
char *tok = malloc(len + 1);
memcpy(tok, p, len);
tok[len] = '\0';
el_val_t result = EL_STR(tok);
/* Note: intentional small leak — token lives for process lifetime */
return result;
}
/*
* gcs_write — upload content to a GCS object.
*
* Uses the GCS JSON upload API (POST, media upload type).
* Returns "ok" on success, "" on failure.
*/
el_val_t gcs_write(el_val_t bucket, el_val_t object_name, el_val_t content) {
el_val_t tok = gcs_get_token();
if (!tok || !*EL_CSTR(tok)) return EL_STR("");
/* URL-encode the object name (replace / with %2F) */
const char *obj = EL_CSTR(object_name);
char encoded[1024];
size_t ei = 0;
for (size_t i = 0; obj[i] && ei < sizeof(encoded) - 4; i++) {
if (obj[i] == '/') { encoded[ei++] = '%'; encoded[ei++] = '2'; encoded[ei++] = 'F'; }
else encoded[ei++] = obj[i];
}
encoded[ei] = '\0';
/* Build upload URL */
char url[2048];
snprintf(url, sizeof(url),
"https://storage.googleapis.com/upload/storage/v1/b/%s/o"
"?uploadType=media&name=%s",
EL_CSTR(bucket), encoded);
/* POST with Authorization + Content-Type headers */
char bearer[2048];
snprintf(bearer, sizeof(bearer), "Bearer %s", EL_CSTR(tok));
el_val_t headers = el_map_new(2,
"Authorization", EL_STR(bearer),
"Content-Type", EL_STR("application/json"));
el_val_t resp = http_post_with_headers(EL_STR(url), content, headers);
if (!resp) return EL_STR("");
/* GCS returns the object metadata JSON on success */
const char *r = EL_CSTR(resp);
if (strstr(r, "\"name\"")) return EL_STR("ok");
return EL_STR("");
}
/*
* gcs_read — download a GCS object's content.
*
* Returns the raw content, or "" if the object does not exist or on error.
*/
el_val_t gcs_read(el_val_t bucket, el_val_t object_name) {
el_val_t tok = gcs_get_token();
if (!tok || !*EL_CSTR(tok)) return EL_STR("");
/* URL-encode object name */
const char *obj = EL_CSTR(object_name);
char encoded[1024];
size_t ei = 0;
for (size_t i = 0; obj[i] && ei < sizeof(encoded) - 4; i++) {
if (obj[i] == '/') { encoded[ei++] = '%'; encoded[ei++] = '2'; encoded[ei++] = 'F'; }
else encoded[ei++] = obj[i];
}
encoded[ei] = '\0';
char url[2048];
snprintf(url, sizeof(url),
"https://storage.googleapis.com/download/storage/v1/b/%s/o/%s?alt=media",
EL_CSTR(bucket), encoded);
char bearer[2048];
snprintf(bearer, sizeof(bearer), "Bearer %s", EL_CSTR(tok));
el_val_t headers = el_map_new(1, "Authorization", EL_STR(bearer));
el_val_t resp = http_get_with_headers(EL_STR(url), headers);
if (!resp) return EL_STR("");
return resp;
}
+158 -64
View File
@@ -406,6 +406,67 @@ fn waitlist_upsert(email: String, name: String, plan: String, source: String, at
return ""
}
// send_email
//
// Canonical Resend send path. Builds a JSON body with proper escaping,
// posts via http_post_auth_json (which sets both Bearer auth AND
// Content-Type: application/json - the missing Content-Type was the
// silent-drop bug), parses the response, and logs the outcome.
//
// Returns:
// {"ok":true,"id":"<resend-id>"} - on success
// {"ok":false,"error":"<message>","raw":"..."} - on Resend 4xx/5xx
// {"ok":false,"error":"empty_response"} - on transport failure
// {"ok":false,"error":"no_resend_key"} - if RESEND_API_KEY unset
//
// `to` is a single recipient string. `subject` is plain text. One of
// `html` or `text` should be non-empty; if both are set both are sent.
// `from_addr` is the full From header value, e.g. "Neuron <no-reply@...>".
// (Parameter is named from_addr - not from - because the build-stage.sh
// combiner step strips lines that match `^[[:space:]]*from`, which would
// nuke any line beginning with `from:`.)
// All four user-provided strings are JSON-escaped before interpolation,
// so callers can pass arbitrary content without pre-escaping.
//
// Keys are NEVER logged - the Bearer token is passed to libcurl via
// curl_slist headers and never echoed.
fn send_email(from_addr: String, to: String, subject: String, html: String, text: String) -> String {
let resend_key: String = state_get("__resend_api_key__")
if str_eq(resend_key, "") {
println("[email] skipped subject=\"" + subject + "\" reason=no_resend_key")
return "{\"ok\":false,\"error\":\"no_resend_key\"}"
}
let from_safe: String = str_replace(str_replace(from_addr, "\\", "\\\\"), "\"", "\\\"")
let to_safe: String = str_replace(str_replace(to, "\\", "\\\\"), "\"", "\\\"")
let subj_safe: String = str_replace(str_replace(str_replace(str_replace(subject, "\\", "\\\\"), "\"", "\\\""), "\n", "\\n"), "\r", "\\r")
let html_safe: String = str_replace(str_replace(str_replace(str_replace(html, "\\", "\\\\"), "\"", "\\\""), "\n", "\\n"), "\r", "\\r")
let text_safe: String = str_replace(str_replace(str_replace(str_replace(text, "\\", "\\\\"), "\"", "\\\""), "\n", "\\n"), "\r", "\\r")
let html_field: String = if str_eq(html, "") { "" } else { ",\"html\":\"" + html_safe + "\"" }
let text_field: String = if str_eq(text, "") { "" } else { ",\"text\":\"" + text_safe + "\"" }
let email_body: String = "{\"from\":\"" + from_safe + "\","
+ "\"to\":[\"" + to_safe + "\"],"
+ "\"subject\":\"" + subj_safe + "\""
+ html_field + text_field + "}"
let resp: String = http_post_auth_json("https://api.resend.com/emails", resend_key, email_body)
if str_eq(resp, "") {
println("[email] FAIL subject=\"" + subject + "\" to=" + to + " error=empty_response")
return "{\"ok\":false,\"error\":\"empty_response\"}"
}
let id: String = json_get(resp, "id")
if !str_eq(id, "") {
println("[email] sent id=" + id + " to=" + to + " subject=\"" + subject + "\"")
return "{\"ok\":true,\"id\":\"" + id + "\"}"
}
// Resend error envelope - {statusCode, name, message}
let err_msg: String = json_get(resp, "message")
let err_name: String = json_get(resp, "name")
let resp_safe: String = str_replace(str_replace(resp, "\\", "\\\\"), "\"", "\\\"")
let err_final: String = if !str_eq(err_msg, "") { err_msg } else if !str_eq(err_name, "") { err_name } else { "unknown_error" }
println("[email] FAIL subject=\"" + subject + "\" to=" + to + " error=\"" + err_final + "\" raw=" + resp)
let err_safe: String = str_replace(str_replace(err_final, "\\", "\\\\"), "\"", "\\\"")
return "{\"ok\":false,\"error\":\"" + err_safe + "\",\"raw\":\"" + resp_safe + "\"}"
}
fn read_asset(abs_path: String) -> String {
let exists: Bool = fs_exists(abs_path)
if !exists {
@@ -855,23 +916,36 @@ fn handle_request(method: String, path: String, body: String) -> String {
// Enterprise inquiry
if str_eq(path, "/api/enterprise-inquiry") {
let resend_key: String = state_get("__resend_api_key__")
let name_val: String = if str_contains(body, "\"name\"") { "submitted" } else { "" }
if str_eq(name_val, "") {
return "{\"error\":\"invalid request\"}"
}
// Log to stdout regardless of email delivery
println("[enterprise-inquiry] " + body)
// Send via Resend if key is configured
if !str_eq(resend_key, "") {
let email_body: String = "{\"from\":\"Neuron Enterprise <enterprise@neurontechnologies.ai>\",\"to\":[\"enterprise@neurontechnologies.ai\"],\"subject\":\"Enterprise Inquiry\",\"text\":" + body + "}"
let resp: String = http_post_auth(
"https://api.resend.com/emails",
resend_key,
email_body
)
println("[enterprise-inquiry] resend: " + resp)
}
// Pull individual fields so we can build a clean text body. The
// previous implementation interpolated the raw request body into
// the JSON `text` field unquoted, which produced malformed JSON
// and was a secondary cause of the silent-drop.
let ent_name: String = json_get(body, "name")
let ent_email: String = json_get(body, "email")
let ent_company: String = json_get(body, "company")
let ent_role: String = json_get(body, "role")
let ent_seats: String = json_get(body, "seats")
let ent_msg: String = json_get(body, "message")
let ent_text: String = "Name: " + ent_name + "\n"
+ "Email: " + ent_email + "\n"
+ "Company: " + ent_company + "\n"
+ "Role: " + ent_role + "\n"
+ "Seats: " + ent_seats + "\n\n"
+ "Message:\n" + ent_msg
let send_resp: String = send_email(
"Neuron Enterprise <enterprise@neurontechnologies.ai>",
"enterprise@neurontechnologies.ai",
"Enterprise Inquiry: " + ent_company,
"",
ent_text
)
println("[enterprise-inquiry] " + send_resp)
return "{\"received\":true}"
}
@@ -887,13 +961,14 @@ fn handle_request(method: String, path: String, body: String) -> String {
// Write to Supabase waitlist table
waitlist_upsert(wl_email, "", "free", "preorder", "", "", 0)
// Email notification
let e_safe: String = str_replace(str_replace(wl_email, "\\", "\\\\"), "\"", "\\\"")
let resend_key: String = state_get("__resend_api_key__")
if !str_eq(resend_key, "") {
let email_body: String = "{\"from\":\"Neuron <no-reply@neurontechnologies.ai>\",\"to\":[\"will.anderson@neurontechnologies.ai\"],\"subject\":\"Free tier preorder: " + e_safe + "\",\"text\":\"New free tier preorder\\nEmail: " + e_safe + "\"}"
let resp: String = http_post_auth("https://api.resend.com/emails", resend_key, email_body)
println("[waitlist] email: " + resp)
}
let wl_send: String = send_email(
"Neuron <no-reply@neurontechnologies.ai>",
"will.anderson@neurontechnologies.ai",
"Free tier preorder: " + wl_email,
"",
"New free tier preorder\nEmail: " + wl_email
)
println("[waitlist] " + wl_send)
return "{\"ok\":true}"
}
@@ -928,19 +1003,25 @@ fn handle_request(method: String, path: String, body: String) -> String {
println("[attest] gcs write " + attest_key + " -> " + gcs_ok)
}
// Email notification
let resend_key: String = state_get("__resend_api_key__")
if !str_eq(resend_key, "") {
let subject: String = "Founding member attestation: " + attest_name + " <" + attest_email + ">"
let email_body: String = "{\"from\":\"Neuron <no-reply@neurontechnologies.ai>\",\"to\":[\"will.anderson@neurontechnologies.ai\"],\"subject\":\"" + str_replace(subject, "\"", "\\\"") + "\",\"text\":\"Plan: " + attest_plan + "\\nName: " + n_safe + "\\nEmail: " + e_safe + "\\nTime: " + attest_ts + "\\n\\nAttestation: " + t_safe + "\"}"
let resp: String = http_post_auth("https://api.resend.com/emails", resend_key, email_body)
println("[attest] email: " + resp)
}
let attest_subject: String = "Founding member attestation: " + attest_name + " <" + attest_email + ">"
let attest_text_body: String = "Plan: " + attest_plan + "\n"
+ "Name: " + attest_name + "\n"
+ "Email: " + attest_email + "\n"
+ "Time: " + attest_ts + "\n\n"
+ "Attestation: " + attest_text
let attest_send: String = send_email(
"Neuron <no-reply@neurontechnologies.ai>",
"will.anderson@neurontechnologies.ai",
attest_subject,
"",
attest_text_body
)
println("[attest] " + attest_send)
return "{\"ok\":true}"
}
// Developer interest form
if str_eq(path, "/api/developer-interest") {
let resend_key: String = state_get("__resend_api_key__")
if !str_contains(body, "\"email\"") {
return "{\"error\":\"invalid request\"}"
}
@@ -950,15 +1031,17 @@ fn handle_request(method: String, path: String, body: String) -> String {
let dev_idea: String = json_get(body, "idea")
waitlist_upsert(dev_email, dev_name, "developer", "developer-interest", dev_idea, "", 0)
println("[developer-interest] " + body)
if !str_eq(resend_key, "") {
let email_body: String = "{\"from\":\"Neuron Developer Program <developers@neurontechnologies.ai>\",\"to\":[\"will.anderson@neurontechnologies.ai\"],\"subject\":\"Developer Interest\",\"text\":" + body + "}"
let resp: String = http_post_auth(
"https://api.resend.com/emails",
resend_key,
email_body
)
println("[developer-interest] resend: " + resp)
}
let dev_text: String = "Name: " + dev_name + "\n"
+ "Email: " + dev_email + "\n\n"
+ "Idea:\n" + dev_idea
let dev_send: String = send_email(
"Neuron Developer Program <developers@neurontechnologies.ai>",
"will.anderson@neurontechnologies.ai",
"Developer Interest: " + dev_name,
"",
dev_text
)
println("[developer-interest] " + dev_send)
return "{\"received\":true}"
}
@@ -1244,27 +1327,27 @@ fn handle_request(method: String, path: String, body: String) -> String {
let ds_is_completed: Bool = str_eq(event_type, "form.completed")
let ds_is_declined: Bool = str_eq(event_type, "form.declined")
if ds_is_completed || ds_is_declined {
let resend_key: String = state_get("__resend_api_key__")
println("[docuseal] gate event=" + event_type + " key=" + (if str_eq(resend_key, "") { "empty" } else { "set" }))
if !str_eq(resend_key, "") {
let subject: String = if str_eq(event_type, "form.completed") {
"DocuSeal: " + signer_email + " signed (#" + sub_id + ")"
} else {
"DocuSeal: " + signer_email + " declined (#" + sub_id + ")"
}
let html: String = "<p>" + name_safe + " &lt;" + signer_email + "&gt;</p>"
+ "<p>Submission #" + sub_id + " - " + event_type + " at " + event_ts + "</p>"
+ "<pre style=\"font-family:monospace;font-size:11px;background:#f4f4f4;padding:12px;border-radius:6px\">"
+ body_safe + "</pre>"
let html_safe: String = str_replace(str_replace(html, "\\", "\\\\"), "\"", "\\\"")
let subject_safe: String = str_replace(subject, "\"", "\\\"")
let email_body: String = "{\"from\":\"DocuSeal <no-reply@neurontechnologies.ai>\","
+ "\"to\":[\"will.anderson@neurontechnologies.ai\"],"
+ "\"subject\":\"" + subject_safe + "\","
+ "\"html\":\"" + html_safe + "\"}"
let mail_resp: String = http_post_auth("https://api.resend.com/emails", resend_key, email_body)
println("[docuseal] resend " + event_type + " -> " + mail_resp)
let ds_subject: String = if str_eq(event_type, "form.completed") {
"DocuSeal: " + signer_email + " signed (#" + sub_id + ")"
} else {
"DocuSeal: " + signer_email + " declined (#" + sub_id + ")"
}
// The send_email helper handles JSON-escape of html/text. We
// pass the raw payload body (body_safe is already DB-escaped,
// but send_email re-escapes for JSON, which is idempotent for
// already-escaped backslash/quote sequences in HTML context).
let ds_html: String = "<p>" + name_safe + " &lt;" + signer_email + "&gt;</p>"
+ "<p>Submission #" + sub_id + " - " + event_type + " at " + event_ts + "</p>"
+ "<pre style=\"font-family:monospace;font-size:11px;background:#f4f4f4;padding:12px;border-radius:6px\">"
+ body_safe + "</pre>"
let ds_send: String = send_email(
"DocuSeal <no-reply@neurontechnologies.ai>",
"will.anderson@neurontechnologies.ai",
ds_subject,
ds_html,
""
)
println("[docuseal] " + event_type + " " + ds_send)
}
return "{\"ok\":true}"
@@ -1316,7 +1399,14 @@ fn handle_request(method: String, path: String, body: String) -> String {
if !str_eq(sb_key, "") {
let q_safe: String = str_replace(str_replace(question, "\\", "\\\\"), "\"", "\\\"")
let a_safe: String = str_replace(str_replace(str_replace(str_replace(answer_plain, "\\", "\\\\"), "\"", "\\\""), "\n", "\\n"), "\r", "\\r")
let card_row: String = "{\"id\":\"" + id + "\",\"question\":\"" + q_safe + "\",\"answer\":\"" + a_safe + "\"}"
// answer_html: marked.js-rendered HTML the client captured,
// sanitized server-side. Storing it lets /said render the
// same formatted preview as /share/<id>. Empty -> omit the
// column (legacy fallback path).
let html_sanitized: String = if str_eq(answer_html_raw, "") { "" } else { sanitize_share_html(answer_html_raw) }
let h_safe: String = str_replace(str_replace(str_replace(str_replace(html_sanitized, "\\", "\\\\"), "\"", "\\\""), "\n", "\\n"), "\r", "\\r")
let html_field: String = if str_eq(html_sanitized, "") { "" } else { ",\"answer_html\":\"" + h_safe + "\"" }
let card_row: String = "{\"id\":\"" + id + "\",\"question\":\"" + q_safe + "\",\"answer\":\"" + a_safe + "\"" + html_field + "}"
let sb_resp: String = supabase_insert(sb_url, sb_key, "share_cards", card_row)
println("[share] supabase insert " + id + " -> " + sb_resp)
}
@@ -1486,7 +1576,7 @@ fn handle_request(method: String, path: String, body: String) -> String {
let cards_json = supabase_get(
sb_url,
sb_key,
"share_cards?select=id,question,answer,score,upvotes,downvotes,created_at&order=score.desc,created_at.desc&limit=100"
"share_cards?select=id,question,answer,answer_html,score,upvotes,downvotes,created_at&order=score.desc,created_at.desc&limit=100"
)
}
return gallery_page(cards_json, sb_url, sb_anon)
@@ -1630,13 +1720,17 @@ fn handle_request(method: String, path: String, body: String) -> String {
let fam_row: String = "{\"parent_email\":\"" + pe_safe + "\",\"parent_user_id\":\"\",\"child_email\":\"" + ce_safe + "\",\"child_dob_year\":" + child_dob_year_str + ",\"status\":\"invited\",\"stripe_subscription_id\":\"" + sub_id + "\",\"invite_token\":\"" + invite_token + "\"}"
let fam_insert_resp: String = supabase_insert(fam_sb_url, fam_sb_key, "family_members", fam_row)
println("[family/invite] insert -> " + fam_insert_resp)
// Send invite email if Resend available
let fam_resend_key: String = state_get("__resend_api_key__")
if !str_eq(fam_resend_key, "") {
let invite_email_body: String = "{\"from\":\"Neuron <no-reply@neurontechnologies.ai>\",\"to\":[\"" + ce_safe + "\"],\"subject\":\"You have been invited to Neuron\",\"text\":\"You have been invited to join Neuron by " + pe_safe + ". Visit https://neurontechnologies.ai/account to set up your account.\"}"
let invite_email_resp: String = http_post_auth("https://api.resend.com/emails", fam_resend_key, invite_email_body)
println("[family/invite] email -> " + invite_email_resp)
}
// Send invite email
let invite_text: String = "You have been invited to join Neuron by " + parent_email
+ ". Visit https://neurontechnologies.ai/account to set up your account."
let invite_send: String = send_email(
"Neuron <no-reply@neurontechnologies.ai>",
child_email,
"You have been invited to Neuron",
"",
invite_text
)
println("[family/invite] " + invite_send)
return "{\"ok\":true,\"invite_token\":\"" + invite_token + "\"}"
}