basquetWi + New ticket
enamel ENAMEL-30

EVO-93: move this app's email-outbox drain off Supabase pg_cron onto a venus systemd timer

Done normal cecoder-enamel-cc

Child of EVO-93 (move app email drains off Supabase pg_cron to venus systemd timers). Scope for this app, three steps, no app-code rewrite: 1. INVENTORY. List every scheduled job this app has and where it runs: SELECT jobid, jobname, schedule, active, command FROM cron.job on your Supabase project, plus the crons array in vercel.json, plus whatever your email transport module actually points at (Mars was defaulting to smtp.gmail.com:587). Post the inventory as an event on this WI. 2. CONFIRM THE ROUTE. Verify your outbox-drain HTTP route accepts an external bearer-authed curl from venus - not just a call originating inside Supabase. If it does not, that is the only app-side code change this epic needs. 3. HAND OFF. DM bin-venus-cc the drain URL and the bearer (DM only, never an aro fan-out, never in a commit or a WI body). bin-venus-cc owns EVO-94, the templated evolutiva-emaildrain@<app> systemd unit that replaces the pg_cron job. Do NOT rebuild the outbox layer. emailOutbox + dedupeKey + exponential backoff + reclaim + maxAttempts + the AFTER INSERT wake trigger are sound and stay. Transport is a SEPARATE, LATER step tracked as EVO-95: outbound port 25 from venus is BLOCKED (measured 2026-09-10), so a venus MTA cannot deliver direct-to-MX today and transport stays on an authenticated 587 relay for now. Do not migrate transport as part of this WI. Why now: Mars's Supabase pg_cron launcher went silent for hours on 2026-09-09 (MARS-698) with no alert - a pg_cron-scheduled drain has no backstop and fails invisibly. UNASSIGNED: no coder lane for this app was live on the hub roster at 2026-09-10 06:40Z. Needs an owner before it can start.

Sub-tickets

No sub-tickets.
+ Add sub-ticket

Activity

  • wi-cli-venus created · 5w ago
  • wi-cli-venus assigned · 5w ago
    coder-enamel-cc
  • wi-cli-venus progress · 5w ago
    STEPS 1 AND 2 DONE, step 2 FAILED, cause is a credential that no longer exists rather than a route defect. STEP 1 INVENTORY, measured 2026-09-11: cron.job has 0 rows on ref eprpywqwzsvrjeaforei. app/vercel.json contains regions only - there is no crons array. Transport points at smtp.gmail.com:587, allow-listed in code (ALLOWED_SMTP_HOSTS), with SMTP_USER/PASS/FROM set encrypted in Vercel Production since 2026-09-11 11:35 and a deploy (d5e3546, then edd9d1a) built after them, so the serving deploy binds them. STEP 2 RESULT: an external bearer-authed POST from venus to https://enamel-evolutiva.pensanta.com/api/cron/drain-email-outbox returns 401 {"error":"unauthorized"}. That JSON is the ROUTE's own body, not a Vercel SSO interstitial, so DNS, routing and the deployment protection posture are all fine and the endpoint is externally reachable. The 401 is GATE-CORRECT: I sent an empty bearer. It is evidence about my call, not about the route, and it must not be read as 'the route rejects external callers'. WHY THE BEARER WAS EMPTY, and this is the actual finding: enamel's CRON_SECRET is unreadable by anyone. vercel env pull returns it as an empty string because Vercel holds it sensitive/write-only; DATABASE_URL and the NEXT_PUBLIC_* vars from THE SAME PULL come back with real values, so this is per-variable and not a permissions problem at this end. SMTP_PASS/USER/FROM behave the same way. The value is not in ltm.md, not in ~/.local/bin/.llmmsg-env, and nowhere in the repo tree including git-ignored files. It was set 41 days ago and did not survive. STEP 3 IS THEREFORE IMPOSSIBLE AS WRITTEN - I cannot hand bin-venus-cc a bearer nobody holds. ENAMEL-33 is parked on this, not on bin-venus-cc's prerequisites answer. PROPOSED FIX, GO requested from pmmaster-evolutiva-cc 2026-09-11 because minting a credential is not an own-initiative action: generate a new CRON_SECRET, set it in Vercel Production, push to produce a deploy that binds it (Vercel binds env at BUILD time - setting it alone does not reach the serving deploy), verify a 200, then DM bin-venus-cc the URL and the bearer. BLAST RADIUS, measured rather than assumed: the drain route is the SOLE consumer of CRON_SECRET in this codebase. support-api-auth.ts names it only in comments, and specifically to say SUPPORT_API_KEY must never reuse it. No live caller holds the current value - there is no invoker at all. A new value breaks nothing. GENERALISES PAST ENAMEL: any app whose cron bearer was set sensitive-only now holds a credential that is alive, working, and permanently unreadable, and nothing reports that until someone needs to CALL the endpoint. mars and venus are cased in evolutiva-emaildrain@.service with no timer; if their bearers were set the same way then 'cutover not scheduled' and 'cutover impossible' are indistinguishable from outside. Raised to bin-venus-cc as an EVO-94 question. terra is the only instance proven unaffected.
  • wi-cli-venus blocked · 5w ago
    Step 3 cannot execute: enamel's CRON_SECRET is unrecoverable (Vercel sensitive/write-only, absent from every store on venus), so there is no bearer to hand bin-venus-cc. Unblocks on a GO to mint a new value - requested from pmmaster-evolutiva-cc 2026-09-11. Steps 1 and 2 are complete.
  • wi-cli-venus unblocked · 5w ago
    GO given by pmmaster-evolutiva-cc 2026-09-11, carrying Elazar authority per his 09:22 announce. Conditions attached to the GO and all met: Production only, ship the binding deploy via gitpush.sh, no vercel deploy and no dashboard deploy and no new project, verify the 200 from venus with the real bearer before handing anything over, DM the value to bin-venus-cc and DM only, never an aro fan-out, never into a commit or a log or a WI body, one git-ignored overlay on the host that needs it, and do not relax the gate to get a 200.
  • wi-cli-venus completed · 5w ago
    ALL THREE STEPS DONE 2026-09-11. Step 1 inventory and step 2 route-confirmation were already recorded; this closes step 3 and corrects step 2 from FAILED to PASSED. STEP 2, NOW PASSED BY MEASUREMENT. A bearer-authed POST from venus to https://enamel-evolutiva.pensanta.com/api/cron/drain-email-outbox returns http=200 with body ok:true, claimed:0, sent:0, skipped:0, failed:0, retried:0. The earlier 401 was gate-correct and was evidence about my caller, never about the route: I had no bearer to send. No code changed and the gate was not relaxed, which was an explicit GO condition. SECOND FINDING IN THE SAME BODY, worth more than the auth result: ok:true rather than reason:config_unavailable means the pre-claim SMTP gate at route.ts:460-479 did NOT fire, so the serving deploy binds SMTP_USER, SMTP_PASS and SMTP_FROM. That measurement retires the emailConfigUnavailable warn-storm precondition recorded on ENAMEL-33. THE CREDENTIAL. Old CRON_SECRET was unrecoverable, so a new 64-hex value was minted with openssl rand -hex 32, set in Vercel Production by rm-then-add, and bound by a push-triggered deploy - Vercel binds env at BUILD time, so setting a variable alone never reaches the serving deploy. Blast radius was measured before minting rather than assumed: the drain route is the SOLE consumer in this codebase, support-api-auth.ts names the variable only in a comment saying SUPPORT_API_KEY must never reuse it, and no live caller held the old value because there was no invoker at all. Nothing broke. THE FINDING THAT OUTLIVES THIS ROW, and the reason the old value died: VERCEL IS NOT A STORE. vercel env pull returns CRON_SECRET and the SMTP trio as EMPTY STRINGS while DATABASE_URL and the NEXT_PUBLIC_ vars from the same pull come back real. The split is by age, not by name - everything created 42 days ago reads back, everything created since does not - and re-adding through vercel env add reproduced it immediately, so this is the platform default now and not a flag anyone set. A pull that succeeds with a blank value is indistinguishable from a variable that was never set, so PULLED THE ENV IS NOT EVIDENCE YOU HOLD THE ENV. The failure has no signal until someone CALLS the endpoint: the credential stays alive and working while becoming permanently unreadable. Durable copy therefore lives outside Vercel, in the venus overlay and in ltm; the rule is that any value set in Vercel is written to its durable store in the same action. STEP 3 HANDOFF, sent to bin-venus-cc by DM 2026-09-11 12:52, tag coder-enamel-cc-mtwybymvizng. ENAMEL_CRON_SECRET is now in the venus overlay ~/.local/bin/.llmmsg-env, named to match the existing mars|venus|terra convention that evolutiva-emaildrain@.service already reads; EMAILDRAIN_ENAMEL_URL was already present there and is correct. Neither value appears in this body, in a commit, or in a log. WHAT IS EXPLICITLY NOT CLOSED BY THIS ROW. The enamel timer is not enabled. evolutiva-emaildrain@.service cases mars|venus|terra only and exits 64 on anything else, so the remaining work is an enamel) case arm plus enabling evolutiva-emaildrain@enamel.timer - a unit edit on venus, owned by bin-venus-cc, tracked on ENAMEL-33. This row was scoped inventory, route-confirmation and handoff, and all three are delivered. ONE HYPOTHESIS OF MINE FALSIFIED, recorded because it was wrong on this row: I proposed that mars and venus sitting cased-but-timerless might mean their bearers were unrecoverable too. bin-venus-cc measured the overlay - CRON_SECRET, VENUS_CRON_SECRET and TERRA_CRON_SECRET are all set and 64 chars. Their not-scheduled-yet is a real not-scheduled-yet. venus specifically is deliberate and on record as VENUS-396: pg_cron job 1 stays scheduled because it is currently the only evidence the drain runs at all, and unscheduling it would remove the signal rather than replace it. Enamel is the case that differs, because ENAMEL-32 already gives it the absence-detector VENUS-398 is still building.
task
5w ago by wi-cli-venus
5w ago
2026-09-11 12:48