Commit Graph

14 Commits

Author SHA1 Message Date
Keysat 4b944ca8ad Mark v0.1.0:76 deployed & verified live (Gmail-DWD digest send) 2026-06-15 20:38:52 -05:00
Keysat 47dfd110a0 Add Gmail-DWD send path for the digest mailer (v0.1.0:76)
The box's existing service-account domain-wide-delegation grant already includes
gmail.compose, which authorizes users.messages.send — verified 2026-06-15 by a
token-mint probe and a live messages.send to grant. So CRM-originated mail can
send through the account that already powers email capture: no SMTP account, no
app password, no admin change.

- backend/email_integration/gmail_send.py: send_via_gmail() impersonates a
  domain user and POSTs users.messages.send (reuses credentials.py + the compose
  scope; mirrors compose.py's REST pattern).
- backend/digest_mailer.py: send_digest() prefers Gmail DWD when enabled, falls
  back to smtp_send otherwise. Sender = CRM_DIGEST_SENDER else first active admin.
- server.py: the admin test endpoint now routes through digest_mailer (so the
  Settings button sends via DWD on the box with zero SMTP config). Recipient
  restriction to the admin set and no-leak error handling preserved.
- test_gmail_send.py: build/send + transport routing (provider + urlopen faked).
  19/19 backend green; s9pk typechecks.

SMTP (v75) stays as the fallback transport. Send-path decision + scope finding
recorded in ROADMAP.md and AGENTS.md.
2026-06-15 20:17:27 -05:00
Keysat e62306be27 Mark v0.1.0:75 deployed & verified live on the box 2026-06-15 18:59:59 -05:00
Keysat 114a94c894 Add Settings 'Send Test Digest Email' button (admin) (v0.1.0:75)
Surface the digest test-send endpoint as a clickable admin control so it can be
exercised on the box without curl. Calls POST /api/admin/digest/test-email and
toasts the result (or a 'configure SMTP first' hint). JSX parse-checked.
2026-06-15 18:55:32 -05:00
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
Keysat 840f927324 Add inbox-check line; align .gitignore with canonical .claude policy
Cross-repo git-hygiene audit remediation: surface ~/Projects/standards/INBOX.md items at session start, and switch .gitignore to the deny-by-default .claude/* block (shared wiring allow-listed) plus the canonical secrets/env lines — per standards/portability.md.
2026-06-14 12:17:16 -05:00
Keysat 7285bb0e52 Add regression tests for v74 fixes; close soft-delete leak in list-view aggregates
Lock in the three v0.1.0:74 security/privacy fixes with regression tests, and
fix a same-class soft-delete leak surfaced while writing them.

- backend/test_assets_traversal.py: boots the real server, proves /assets/
  path-traversal vectors (incl. a real decoy file and the live crm.db, plain
  and URL-encoded) 404 and leak nothing, while a legit asset still serves 200.
- backend/test_soft_delete_reads.py: get-by-id 404s soft-deleted rows and
  nested + list-view aggregates exclude soft-deleted children.
- backend/mcp/test_outreach_redaction.py: an unknown free-prose name is
  tokenized away from the Claude payload but re-hydrated locally, and the path
  fails closed (no Claude call) when the local NER model is down.
- backend/run_tests.py: aggregate runner (each backend/**/test_*.py in its own
  subprocess); replaces the manual for-loop. 16/16 green.

A reviewer pass on the tests confirmed the soft-delete filter was missing from
list-view aggregate sub-selects: org contact_count/total_funded and contacts
comm_count/last_contact_date counted soft-deleted rows. Add `deleted_at IS NULL`
to those four (server.py) and regression-cover them.

The reports subsystem (dashboard/pipeline/LP-breakdown, ~16 aggregate queries)
has the same leak and is logged as P2 for a dedicated pass. Not yet built or
deployed — bump the package version before the next s9pk build.
2026-06-13 00:26:22 -05:00
Keysat 6816d4a4f0 Realign stale thesis tests to the 7-member positioning group
ensure_positioning_framings adds 5 Architect framings to the core
positioning variant group alongside Option A/B, so the group holds 7
candidates and choose_variant retires 6. The two thesis tests still
asserted the pre-framings count of 2 — the tests were stale, not the
seed. Realign them, document the 2+5=7 seed structure in the thesis
guide, and refresh AGENTS.md Current state (13/13 tests green).
2026-06-12 18:44:14 -05:00
Keysat a9611b935b Update Current state: v0.1.0:74 deployed to the box
Reflect that the security/privacy hardening is pushed, built, and installed
(box now reports v0.1.0:74); reset Next to live post-deploy verification.
2026-06-12 18:13:13 -05:00
Keysat aec2b7775b Harden privacy boundary and asset serving (v0.1.0:74)
Fixes from the 2026-06-12 full-eval (P0 + two P1s); code-only, no schema
change. Without these the "private CRM" premise was breachable on the LAN:

- P0: the /assets/ route joined the request path onto FRONTEND_DIR without
  normalizing '..' (get_path/urlparse pass it through), so an unauthenticated
  GET /assets/../../data/crm.db read any file the process could — the LP DB,
  the JWT signing secret (-> admin-token forgery), the Gmail key. Add a realpath
  containment check that 404s anything resolving outside FRONTEND_ROOT.
- P1: the LP-outreach drafter built its redaction Boundary with no ner_fn, so
  unknown people/firms in raw email bodies reached Claude in the clear. Pass the
  local-Qwen NER backstop (ner_fn=_ner_local), matching architect_grounding;
  fails closed via the existing scrub_unavailable path if the local model is down.
- P1: get-by-id handlers leaked soft-deleted records by direct ID. Add
  deleted_at IS NULL to every get-by-id path — contacts, organizations,
  opportunities, lp_profiles — and to the nested related-data sub-selects in
  the contact/opportunity detail payloads, matching the list-handler convention.

Bumps the package to v0.1.0:74 (utils.ts + versions/v0.1.0.74.ts + graph).
Full report in EVALUATION.md; remaining P2/P3 triaged in AGENTS.md Current state.
2026-06-12 18:01:48 -05:00
Keysat 1959c22e19 docs: reframe AGENTS.md — venture CRM foundation + agentic layer on top 2026-06-12 17:02:48 -05:00
Keysat 090416f05e docs: extract subsystem guides; keep AGENTS.md to whole-repo facts
Move subsystem mechanics (migrations, thesis gate, redaction, ingest,
email, packaging) out of AGENTS.md into docs/guides/<topic>.md, each
scoped by paths: frontmatter and symlinked from .claude/rules/ so Claude
Code lazy-loads them. AGENTS.md keeps whole-repo facts and universal
guardrails plus a one-line index per guide. Fix the inaccurate
".claude/ is gitignored" note — it is tracked.
2026-06-12 16:46:49 -05:00
Keysat cabbcae5d5 docs: scrub box hostname to $START9_BOX_HOST; add Current state section
Replace the hardcoded immense-voyage.local with $START9_BOX_HOST so the
real host lives only in local start-cli context, not the repo. Add a
Current state section for fast session orientation.
2026-06-12 16:36:34 -05:00
Keysat ef869be082 docs: add AGENTS.md as canonical agent guide; symlink CLAUDE.md
Add a concise day-one AGENTS.md (stack, exact build/run/test/deploy
commands, directory layout, conventions, Always/Never). Preserve the
existing CLAUDE.md project constitution as docs/ten31-constitution.md
(referenced from AGENTS.md) and point CLAUDE.md -> AGENTS.md so Claude
Code loads the canonical guide.
2026-06-12 16:23:10 -05:00