Files
Keysat 2758ac81d3 Add daily-digest Phase A: per-package SMTP send + admin test endpoint (v0.1.0:75)
Groundwork for the daily activity digest: give the CRM an outbound mail path.
Today nothing leaves the box (Gmail capture + drafts only), so this adds a
dedicated, per-package SMTP account independent of any StartOS system-wide SMTP.

- configureDigestSmtp Start9 action: writes host/port/from/username/password/
  security to /data/secrets/smtp/* (password piped over stdin, never argv/env;
  per-field files, owner-only) — mirrors the setAnthropicApiKey pattern.
- docker_entrypoint.sh reads those at boot and exports SMTP_* (operator env wins).
- backend/smtp_send.py: stdlib smtplib wrapper reading SMTP_* (one code path for
  dev .env and the box); starttls/tls/none modes.
- POST /api/admin/digest/test-email (admin-only): proves the pipe. Recipients are
  restricted to the active-admin set — an arbitrary `to` is rejected, so the
  endpoint is not an open relay; send failures are logged, not echoed (an SMTP
  auth error can carry the credential).
- Tests: test_smtp_send.py (sender), test_smtp_endpoint.py (gating + relay
  restriction + no-leak). 18/18 backend green; s9pk typechecks.

Analysis/summarization for the digest body (Phase B) will run on Spark, never
Claude — the digest is deliberately un-anonymized. Decisions + Phase B plan in
ROADMAP.md.
2026-06-15 18:33:06 -05:00

27 lines
1.4 KiB
TypeScript

import { VersionInfo } from '@start9labs/start-sdk'
// Phase-A of the daily activity digest: outbound SMTP send capability. Code-only,
// no schema change (migrations are no-ops):
// * New "Configure Digest SMTP" StartOS action (actions/configureDigestSmtp.ts):
// writes a per-package, custom SMTP account to /data/secrets/smtp/{host,port,
// from,username,password,security} — independent of any StartOS system-wide
// SMTP account. Password is piped over stdin (never argv/env), like the
// Anthropic-key action.
// * docker_entrypoint.sh reads those files at boot and exports SMTP_* into the
// server process (env still wins for an operator override).
// * backend/smtp_send.py: stdlib smtplib wrapper reading SMTP_* (one code path
// for dev .env and the box). New admin endpoint POST /api/admin/digest/test-email
// sends a test message to the requesting `to` or to all active admins, to prove
// the pipe before the digest itself (Phase B) is built.
export const v_0_1_0_75 = VersionInfo.of({
version: '0.1.0:75',
releaseNotes: {
en_US: [
'Add outbound email: a "Configure Digest SMTP" action sets a dedicated mailbox for this',
'service (no server-wide SMTP account required), and a new admin "send test email" action',
'verifies it works — groundwork for the daily activity digest.',
].join(' '),
},
migrations: { up: async () => {}, down: async () => {} },
})