Skip to content

feat(security): out-of-band consent + security-notification channel (email + webhook) #330

Description

@cmeans-claude-dev

Summary

General awareness-to-user outbound communication channel (email + webhook in Phase 1). Item 4 in the revised pre-beta sequence (per pre-beta-plan-synthesis-2026-04-17).

In-chat Ask only works when a human is in a session. Many events don't have an attached chat — scheduled fires, edge processes, async webhooks, cross-user directives, security events. Outbound delivery is needed for those cases, plus for security notifications from the prompt-injection scanner (e.g., zero-width stripping events must surface at warning severity, never silently).

Phase 1 message types

  1. Consent requests — with callback URLs, response options
  2. Security notifications — informational or warning-level, no response needed

Phase 2+: briefing push summaries, intention-fire notifications without consent, shared-entry notifications, weekly digests.

Underlying plumbing (HMAC, signed URLs, retries, idempotency, debug mode) applies uniformly across message types.

Severity levels

  • info — happened, you should know eventually. Batched. Canvas default. Email opt-in.
  • warning — happened, might matter. Fires immediately. Routes to primary channel.
  • alert — something is wrong. Fires immediately. Routes to primary channel (with escalation in later phases).

Severity assignments

  • Zero-width character stripping → warning (any stripping means the payload arrived with adversarial shape; the rest deserves scrutiny)
  • Any defensive transformation applied to user content → warning minimum
  • Directive scan_status → flagged → warning
  • Directive scan_status → cleaned → warning (inherits from stripping)
  • Gate blocks on Never policies → info
  • Token auth failure, single → info
  • Token auth failure, repeated within window → warning (may escalate to alert)
  • Kill switch triggered → alert
  • Repeated warnings from same source within short window → escalate to alert

Silent Defense Principle (explicit)

Defensive actions on user content must be visible and auditable, never silent. Any defensive action means the entry is less trusted than a clean arrival. Stripping is not a full fix — it addresses the specific known-bad pattern but leaves the rest of the payload under elevated suspicion.

This principle drives:

Content arrives with hidden characters for a reason: source compromise, third-party pipeline shipping adversarial formatting, copy-paste from a compromised source, or legitimate-but-unexpected formatting. All four deserve user attention. Even the benign case reveals something about the data pipeline the user didn't know.

Consent request message type

Structured object: request_id, summary, blast_radius, reversibility, provenance, urgency, expiration, variable-length response_options (each with label, callback_url, semantic_type). Callback URLs are signed, single-use, time-limited.

Summary field must come from server-generated structured metadata, not directly from the requesting agent or compromised source (applies to both message types).

Security notification message type

Simpler than consent. No callback URLs, no response options:

  • message_id (stable, for dedup)
  • message_type: "security_notification"
  • severity (info | warning | alert)
  • summary (plain-language, short)
  • detail (optional context)
  • source (who/what caused the event)
  • count (for batched info-level events)
  • first_occurred_at, last_occurred_at
  • canvas_link (back to Canvas for full context)
  • created_at

First concrete use case: zero-width character stripping, at warning level.

Batching rules

  • info — batched per source, configurable window (default 5–10 min). One delivery per batch summarizing all events.
  • warning and above — no batching. Fire immediately.
  • Escalation — repeated warnings from one source within short window → alert. Phase 1 default: "3 warnings within 5 minutes from a single source becomes alert."
  • Rate limit — per-user hourly cap on total notification volume, regardless of severity. Hard cap prevents compromised source flooding. Limit should be generous so legitimate high-activity doesn't suppress warnings.

Routing defaults

  • Consent requests → primary channel (webhook or email, whichever configured)
  • Security notifications info → Canvas only; user can opt in to email
  • Security notifications warning → primary channel + Canvas
  • Security notifications alert → primary channel + Canvas; user can opt in to secondary channels
  • Canvas always receives all messages regardless of other routing. Canvas is the source of truth; outbound channels are layers on top.

Delivery channel staircase

  • Phase 1 — Email (SMTP) + Webhook as peers
  • Post-beta — Web push
  • Post-beta (paid) — SMS
  • Far future — native apps

Webhook payload schema

Defined as a system-owned schema entry (reuses v0.18.0 schema infrastructure). Canonical schema; users build receivers to match. Receivers must handle at least message_type: consent_request and message_type: security_notification for Phase 1.

Conservative content defaults, user-configurable to more detail. Default payload is metadata only (summary, severity, source, counts, timestamps, canvas_link). No raw entry content. Users opt in to richer payloads if the receiver is trusted.

Authentication

  • Outbound (awareness → receiver) — HMAC signature over payload, pre-shared secret
  • Inbound (callback → awareness) — signed URLs (single-use, time-limited) for consent; optional authenticated callback for high-consequence (tier-differentiated)
  • Rate limits — outbound (don't spam broken receivers), inbound (flood protection)

Delivery semantics

  • Retry — 3 retries with exponential backoff, then hard-fail. Configurable.
  • Fallback chain — user-configurable ("try webhook, fall back to email after 2 min, hard-fail after 10 min")
  • Idempotencyrequest_id or message_id enables receiver-side dedup
  • Ordering — no guarantee

Debug mode

Dry-run toggle (inert callbacks, test-flagged notifications), canonical test payloads, delivery logs, signature verification tool, replay tool.

Canvas integration

Canvas always shows:

  • Pending-consents view
  • Security notifications log (filterable by severity, source, time)
  • Delivery log (what sent to which channel, response codes)

Phase 1 scope (pre-beta minimum viable)

  • Canonical outbound payload schema supporting both message types (registered as a _system schema entry)
  • Consent request primitive
  • Security notification primitive
  • Severity-based routing (info / warning / alert)
  • Batching for info-level (per-source window)
  • Rate limits per user, per source
  • Escalation rules (repeated warnings → alert)
  • HMAC-signed outbound, URL-signed inbound
  • Retry policy with exponential backoff
  • User routing preferences (simple defaults + user override)
  • Dry-run debug mode
  • Canvas: pending-consents view, notifications log, delivery log
  • Email channel (SMTP)
  • Webhook channel (POST to user URL)

Deferred

  • Web push, SMS, native apps
  • Multi-approver consent
  • User-batched consent
  • Authenticated-callback tier for high-consequence
  • Per-source fallback rules
  • Additional message types (intention fires, briefing push, digests)
  • Revocation-after-approval

Sequencing

Depends on:

Can ship without:

  • Sharing
  • Full event-driven intentions
  • Channels beyond email and webhook

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1: highFix soon — impacting users or blocking near-term goalsenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions