Harden two-factor designation spec and privacy policy for Twilio flow
This commit is contained in:
parent
d015135073
commit
29e5ae2a4c
@ -1,76 +1,72 @@
|
|||||||
# Edut Secret System - Two-Factor Designation Spec
|
# Edut Secret System - Two-Factor Designation Spec (Hardened)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The Edut designation flow is a two-factor protocol:
|
The Edut designation flow is a two-factor protocol:
|
||||||
|
|
||||||
1. Phone verification by SMS (Twilio).
|
1. Phone verification by SMS (Twilio).
|
||||||
2. Email verification by classified mailto request (Mailgun).
|
2. Email verification by protocol email (Mailgun).
|
||||||
|
|
||||||
Both factors bind to the same designation code and auth token. This is not a throwaway waitlist. It is the pre-launch identity envelope that can transition into launch activation workflows.
|
Both factors are bound to one server-generated designation record.
|
||||||
|
This is a pre-launch identity bootstrap used for launch activation continuity.
|
||||||
|
|
||||||
## End-to-End Flow
|
## User Experience Sequence (Continue Flow)
|
||||||
|
|
||||||
|
1. Initial page state: orb, identity text, footer links.
|
||||||
|
2. First click anywhere: globe spin intensifies, `continue` appears.
|
||||||
|
3. Click `continue`: minimal phone input appears.
|
||||||
|
4. User submits phone: page calls `POST /secret/initiate`.
|
||||||
|
5. API returns status ticket + expiry. UI enters ambient waiting state.
|
||||||
|
6. User receives SMS and replies `CONFIRM`.
|
||||||
|
7. Page polls `GET /secret/status` using the status ticket.
|
||||||
|
8. On `phone_verified`, page attempts `mailto` open.
|
||||||
|
9. If browser blocks `mailto`, show explicit fallback action: `continue to email`.
|
||||||
|
10. User sends email request.
|
||||||
|
11. Mailgun webhook verifies and marks email complete.
|
||||||
|
12. UI shows `acknowledged · {token}` and stores local marker.
|
||||||
|
|
||||||
|
Privacy and Terms links bypass flow and navigate normally.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
Visitor clicks orb on edut.ai
|
Landing page -> POST /secret/initiate
|
||||||
-> phone input appears
|
-> SMS outbound via Twilio
|
||||||
-> user submits phone
|
User reply CONFIRM -> POST /twilio/inbound
|
||||||
POST /secret/initiate
|
Landing page poll (ticket-bound) -> GET /secret/status
|
||||||
-> code issued
|
phone verified -> mailto compose (auto attempt + fallback button)
|
||||||
-> row created/updated in SQLite
|
User sends email -> POST /mailgun/inbound
|
||||||
-> SMS sent via Twilio
|
Mailgun reply -> classified confirmation email
|
||||||
User receives SMS and replies: CONFIRM
|
|
||||||
Twilio POST /twilio/inbound
|
|
||||||
-> signature verified
|
|
||||||
-> pending designation matched
|
|
||||||
-> phone_verified_at set
|
|
||||||
Landing page polls /secret/status/{code}
|
|
||||||
-> sees phone_verified=true
|
|
||||||
-> opens mailto:{code}@secret.edut.ai with protocol body
|
|
||||||
User sends email
|
|
||||||
Mailgun POST /mailgun/inbound
|
|
||||||
-> signature verified
|
|
||||||
-> code parsed from recipient local-part
|
|
||||||
-> email_verified_at set
|
|
||||||
-> classified confirmation reply sent
|
|
||||||
Landing page stores acknowledged token
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Infrastructure
|
## Infrastructure
|
||||||
|
|
||||||
| Service | Domain / System | Purpose |
|
| Service | Domain / Endpoint | Purpose |
|
||||||
|---------|------------------|---------|
|
|---------|-------------------|---------|
|
||||||
| Landing UI | `edut.ai`, `edut.dev` | Orb, phone capture, mailto initiation |
|
| Landing UI | `edut.ai`, `edut.dev` | Continue flow + phone capture + mailto bridge |
|
||||||
| Twilio | Toll-free SMS number | Outbound designation SMS + inbound CONFIRM replies |
|
| API | `api.edut.ai/secret/*` | Initiate + status polling |
|
||||||
| Mailgun | `secret.edut.ai` | Inbound email processing + confirmation replies |
|
| Twilio | `api.edut.ai/twilio/inbound` | SMS outbound/inbound verification |
|
||||||
| API host | `api.edut.ai` | `/secret/*`, `/twilio/inbound`, `/mailgun/inbound` |
|
| Mailgun | `api.edut.ai/mailgun/inbound` + `secret.edut.ai` | Inbound designation email + confirmation reply |
|
||||||
| Database | SQLite (`/var/lib/edut/secrets.db`) | Durable designation state |
|
| Database | `/var/lib/edut/secrets.db` | Durable designation state |
|
||||||
|
|
||||||
## Twilio Channel Design
|
## State Machine (Deterministic)
|
||||||
|
|
||||||
### Number strategy
|
`pending_phone` -> `phone_verified` -> `pending_email` -> `acknowledged`
|
||||||
|
|
||||||
- Use a toll-free SMS number.
|
Additional terminal/side states:
|
||||||
- Voice is disabled or rejected.
|
|
||||||
- Complete toll-free verification before production traffic.
|
|
||||||
|
|
||||||
### Outbound SMS template
|
- `expired` (ticket or flow timed out)
|
||||||
|
- `abandoned` (no progress within retention window)
|
||||||
|
- `opted_out` (STOP/UNSUBSCRIBE received)
|
||||||
|
- `rate_limited` (temporary)
|
||||||
|
|
||||||
```
|
Rules:
|
||||||
━━━━━━━━━━━━━━━━━
|
|
||||||
EDUT GOVERNANCE PROTOCOL
|
|
||||||
|
|
||||||
Designation: {token}
|
1. No backwards transition except administrative recovery event with audit entry.
|
||||||
Reply CONFIRM to proceed.
|
2. Email verification is blocked unless state is `pending_email`.
|
||||||
━━━━━━━━━━━━━━━━━
|
3. `acknowledged` is terminal for this flow.
|
||||||
```
|
|
||||||
|
|
||||||
Where `{token}` is formatted from the code:
|
## API Contracts
|
||||||
|
|
||||||
- code: `0217073045482`
|
|
||||||
- token: `0217-0730-4548-2`
|
|
||||||
|
|
||||||
## API Surface
|
|
||||||
|
|
||||||
## 1) Initiate
|
## 1) Initiate
|
||||||
|
|
||||||
@ -81,100 +77,128 @@ Request JSON:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"phone": "+12065550123",
|
"phone": "+12065550123",
|
||||||
"origin": "https://edut.ai"
|
"origin": "https://edut.ai",
|
||||||
|
"locale": "en"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Behavior:
|
Behavior:
|
||||||
|
|
||||||
1. Normalize phone to E.164.
|
1. Normalize phone to E.164.
|
||||||
2. Rate-limit by IP, phone, and rolling time window.
|
2. Enforce rate limits (IP + phone + rolling windows).
|
||||||
3. Generate designation code (`MMDDHHmmssmmm`) and auth token.
|
3. Server generates `code` and `auth_token`.
|
||||||
4. Insert or upsert designation row with `phone` and `created_at`.
|
4. Ignore/reject any client-supplied code/token fields.
|
||||||
5. Send SMS via Twilio.
|
5. Create designation row in `pending_phone` state.
|
||||||
6. Return `200` with minimal response:
|
6. Issue short-lived polling ticket.
|
||||||
|
7. Send SMS via Twilio.
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"code": "0217073045482",
|
"status": "sms_sent",
|
||||||
"token": "0217-0730-4548-2",
|
"status_ticket": "st_...",
|
||||||
"status": "sms_sent"
|
"ticket_expires_at": "2026-02-17T07:40:45Z",
|
||||||
|
"display_token": "0217-0730-4548-2"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
- Do not expose internal row id.
|
- Do not return `auth_token`.
|
||||||
- Never return auth token in API responses.
|
- Do not return internal row id.
|
||||||
|
- `display_token` is presentation-only.
|
||||||
|
|
||||||
## 2) Status
|
## 2) Status
|
||||||
|
|
||||||
### `GET /secret/status/{code}`
|
### `GET /secret/status`
|
||||||
|
|
||||||
Response JSON:
|
Auth:
|
||||||
|
|
||||||
|
- `Authorization: Bearer {status_ticket}`
|
||||||
|
|
||||||
|
Behavior:
|
||||||
|
|
||||||
|
1. Validate ticket existence and expiry.
|
||||||
|
2. Enforce ticket + IP rate limits.
|
||||||
|
3. Return minimal status payload.
|
||||||
|
4. Ticket is reusable during TTL for polling.
|
||||||
|
5. Ticket is invalidated at terminal state (`acknowledged`, `expired`, `opted_out`) or TTL expiry.
|
||||||
|
|
||||||
|
Response example:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"code": "0217073045482",
|
"status": "phone_verified",
|
||||||
"phone_verified": true,
|
"phone_verified": true,
|
||||||
"email_verified": false,
|
"email_verified": false,
|
||||||
"status": "phone_verified"
|
"mailto": {
|
||||||
|
"enabled": true,
|
||||||
|
"recipient": "0217073045482@secret.edut.ai",
|
||||||
|
"subject": "EDUT-0217073045482",
|
||||||
|
"body": "..."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Rules:
|
Security notes:
|
||||||
|
|
||||||
- Polling endpoint is read-only.
|
- No code in URL path.
|
||||||
- Response is minimal and does not expose phone/email values.
|
- No phone/email values in response.
|
||||||
- Apply rate limits to prevent enumeration.
|
|
||||||
|
|
||||||
## 3) Twilio inbound
|
## Twilio Webhook
|
||||||
|
|
||||||
### `POST /twilio/inbound`
|
### `POST /twilio/inbound`
|
||||||
|
|
||||||
Expected Twilio fields include:
|
Required checks:
|
||||||
|
|
||||||
- `From` (sender phone)
|
1. Verify `X-Twilio-Signature` with `TWILIO_AUTH_TOKEN`.
|
||||||
- `To` (Edut toll-free number)
|
2. Idempotency on `MessageSid`.
|
||||||
- `Body` (message body)
|
3. Normalize `From` number.
|
||||||
- `MessageSid`
|
|
||||||
|
|
||||||
Verification:
|
Accepted commands (case-insensitive):
|
||||||
|
|
||||||
- Validate `X-Twilio-Signature` against Twilio auth token.
|
- `CONFIRM` -> verify phone and transition to `pending_email`.
|
||||||
- Reject unsigned or invalid payloads.
|
- `STOP`, `UNSUBSCRIBE`, `CANCEL`, `END`, `QUIT` -> transition to `opted_out`.
|
||||||
|
- `HELP` -> send support/help response.
|
||||||
|
|
||||||
Processing:
|
Processing rules:
|
||||||
|
|
||||||
1. Normalize sender phone.
|
- `CONFIRM` applies to latest active designation for that phone within validity window.
|
||||||
2. Accept `CONFIRM` (case-insensitive, trim whitespace).
|
- Return `2xx` only after persistence or deterministic dedupe.
|
||||||
3. Find latest pending designation for that phone within validity window.
|
|
||||||
4. Idempotency key: `MessageSid`.
|
|
||||||
5. Mark `phone_verified_at` and set state `phone_verified`.
|
|
||||||
6. Return `2xx` only after persistence.
|
|
||||||
|
|
||||||
## 4) Mailgun inbound
|
## Mailgun Webhook
|
||||||
|
|
||||||
### `POST /mailgun/inbound`
|
### `POST /mailgun/inbound`
|
||||||
|
|
||||||
Expected Mailgun fields include:
|
Required checks:
|
||||||
|
|
||||||
- `recipient` (`{code}@secret.edut.ai`)
|
1. Verify Mailgun signature with `MAILGUN_SIGNING_KEY`.
|
||||||
- `sender`
|
2. Idempotency on `Message-Id` (or provider event id fallback).
|
||||||
- `Message-Id`
|
3. Parse code from recipient local-part first (`{code}@secret.edut.ai`).
|
||||||
- `subject`
|
|
||||||
|
|
||||||
Verification:
|
Processing rules:
|
||||||
|
|
||||||
- Verify Mailgun webhook signature using Mailgun signing key.
|
1. Match designation by code where state is `pending_email`.
|
||||||
|
2. Record sender and verification timestamp.
|
||||||
|
3. Set state to `acknowledged`.
|
||||||
|
4. Send confirmation reply email.
|
||||||
|
|
||||||
Processing:
|
## SMS Content (Compliance-Safe)
|
||||||
|
|
||||||
1. Parse `code` from recipient local-part.
|
Use plain-text SMS (no heavy unicode framing):
|
||||||
2. Require existing designation row with `phone_verified_at IS NOT NULL`.
|
|
||||||
3. Record `email`, `message_id`, and `email_verified_at`.
|
```
|
||||||
4. Idempotency key: `Message-Id` (or Mailgun event id fallback).
|
EDUT GOVERNANCE PROTOCOL
|
||||||
5. Send confirmation reply through Mailgun.
|
Designation: {display_token}
|
||||||
|
Reply CONFIRM to proceed.
|
||||||
|
Reply STOP to opt out. HELP for support.
|
||||||
|
```
|
||||||
|
|
||||||
|
Rationale:
|
||||||
|
|
||||||
|
- Better carrier compatibility.
|
||||||
|
- Clear STOP/HELP semantics.
|
||||||
|
|
||||||
## Confirmation Email Template
|
## Confirmation Email Template
|
||||||
|
|
||||||
@ -225,6 +249,8 @@ CREATE TABLE IF NOT EXISTS designations (
|
|||||||
message_id TEXT,
|
message_id TEXT,
|
||||||
auth_token TEXT NOT NULL,
|
auth_token TEXT NOT NULL,
|
||||||
status TEXT NOT NULL DEFAULT 'pending_phone',
|
status TEXT NOT NULL DEFAULT 'pending_phone',
|
||||||
|
status_ticket TEXT,
|
||||||
|
ticket_expires_at DATETIME,
|
||||||
created_at DATETIME DEFAULT (datetime('now')),
|
created_at DATETIME DEFAULT (datetime('now')),
|
||||||
replied_at DATETIME,
|
replied_at DATETIME,
|
||||||
reply_status TEXT DEFAULT 'pending'
|
reply_status TEXT DEFAULT 'pending'
|
||||||
@ -234,10 +260,12 @@ CREATE INDEX idx_designations_code ON designations(code);
|
|||||||
CREATE INDEX idx_designations_phone ON designations(phone);
|
CREATE INDEX idx_designations_phone ON designations(phone);
|
||||||
CREATE INDEX idx_designations_email ON designations(email);
|
CREATE INDEX idx_designations_email ON designations(email);
|
||||||
CREATE INDEX idx_designations_status ON designations(status);
|
CREATE INDEX idx_designations_status ON designations(status);
|
||||||
|
CREATE INDEX idx_designations_ticket ON designations(status_ticket);
|
||||||
CREATE INDEX idx_designations_created ON designations(created_at);
|
CREATE INDEX idx_designations_created ON designations(created_at);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS twilio_inbound_events (
|
CREATE TABLE IF NOT EXISTS twilio_inbound_events (
|
||||||
message_sid TEXT PRIMARY KEY,
|
message_sid TEXT PRIMARY KEY,
|
||||||
|
designation_code TEXT,
|
||||||
from_phone TEXT NOT NULL,
|
from_phone TEXT NOT NULL,
|
||||||
body TEXT,
|
body TEXT,
|
||||||
received_at DATETIME DEFAULT (datetime('now'))
|
received_at DATETIME DEFAULT (datetime('now'))
|
||||||
@ -245,6 +273,7 @@ CREATE TABLE IF NOT EXISTS twilio_inbound_events (
|
|||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS mailgun_inbound_events (
|
CREATE TABLE IF NOT EXISTS mailgun_inbound_events (
|
||||||
message_id TEXT PRIMARY KEY,
|
message_id TEXT PRIMARY KEY,
|
||||||
|
designation_code TEXT,
|
||||||
sender TEXT,
|
sender TEXT,
|
||||||
recipient TEXT,
|
recipient TEXT,
|
||||||
received_at DATETIME DEFAULT (datetime('now'))
|
received_at DATETIME DEFAULT (datetime('now'))
|
||||||
@ -303,30 +332,33 @@ MAILGUN_DOMAIN=secret.edut.ai
|
|||||||
MAILGUN_SIGNING_KEY=<webhook signing key>
|
MAILGUN_SIGNING_KEY=<webhook signing key>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Error Handling and Idempotency
|
## Error Handling + Retry Rules
|
||||||
|
|
||||||
- Twilio signature failure: return `403`.
|
- Invalid Twilio signature: `403`.
|
||||||
- Mailgun signature failure: return `403`.
|
- Invalid Mailgun signature: `403`.
|
||||||
- Duplicate inbound event id: treat as idempotent success.
|
- Duplicate inbound event id: idempotent `2xx`.
|
||||||
- Transient DB/send failure: return `5xx` so provider retries.
|
- Transient DB/send failure: `5xx` so provider retries.
|
||||||
- Unknown or expired confirmation attempts: return `200` and no state mutation.
|
- Unknown/expired status ticket: `401` or `410`.
|
||||||
|
- Unknown confirmation attempt: `200` with no state mutation.
|
||||||
|
|
||||||
## Security Controls
|
## Security Controls
|
||||||
|
|
||||||
- Signature verification on both inbound providers.
|
- Signature verification for Twilio and Mailgun.
|
||||||
- Per-IP and per-phone initiate throttles.
|
- Strict rate limits on initiate and status endpoints.
|
||||||
- Per-code polling throttles.
|
- Ticket-bound polling only; no direct code enumeration.
|
||||||
- Do not expose auth token except in confirmation email.
|
- Auth token never exposed via status/initiate API responses.
|
||||||
- Back up SQLite and protect file permissions.
|
- SMS STOP/HELP compliance enabled.
|
||||||
|
- SQLite file encryption-at-rest where host supports it, plus regular backups.
|
||||||
|
|
||||||
## Launch Evolution
|
## Launch Evolution
|
||||||
|
|
||||||
At launch, each fully verified designation (`phone_verified_at` + `email_verified_at`) is eligible for activation messaging:
|
At launch, only fully verified designations (`phone_verified_at` + `email_verified_at`) are eligible for access-level change messaging.
|
||||||
|
|
||||||
- Classification can transition from `OBSERVER` to `OPERATOR`.
|
- Classification may transition `OBSERVER -> OPERATOR`.
|
||||||
- Auth token remains stable.
|
- Auth token remains stable.
|
||||||
- Message can include deployment handoff link or activation instructions.
|
- Activation handoff can attach to this identity envelope.
|
||||||
|
|
||||||
## Important Boundary
|
## Important Boundary
|
||||||
|
|
||||||
This two-factor designation flow is a pre-launch identity bootstrap. It does not replace runtime device trust anchors, workspace isolation controls, or offline license enforcement in the product runtime.
|
This two-factor designation flow is a pre-launch identity bootstrap.
|
||||||
|
It does not replace runtime trust anchors, workspace isolation, or offline license/runtime enforcement in the product runtime.
|
||||||
|
|||||||
@ -10,20 +10,20 @@
|
|||||||
|
|
||||||
The site should feel precise and established while avoiding disclosure of private implementation IP.
|
The site should feel precise and established while avoiding disclosure of private implementation IP.
|
||||||
|
|
||||||
## Primary Experience (Two-Factor Designation)
|
## Primary Experience (Two-Factor, Continue Flow)
|
||||||
|
|
||||||
1. A visitor lands on `edut.ai`.
|
1. A visitor lands on `edut.ai`.
|
||||||
2. They see only the globe, identity line, and meaning line.
|
2. They see the orb, identity line, and footer links.
|
||||||
3. They click anywhere.
|
3. First click anywhere increases orb spin and reveals `continue`.
|
||||||
4. A minimal phone field appears inline (same aesthetic language).
|
4. Clicking `continue` reveals minimal phone input.
|
||||||
5. They enter their number and submit.
|
5. On submit, server initiates SMS verification and returns a short-lived status ticket.
|
||||||
6. They receive SMS from Edut protocol channel with designation token and `CONFIRM` instruction.
|
6. User replies `CONFIRM` to protocol SMS.
|
||||||
7. They reply `CONFIRM`.
|
7. Page polls status using ticket-bound auth.
|
||||||
8. Once phone verification is detected, the page opens the classified mailto request for the same designation code.
|
8. On phone verification, page attempts to open protocol email compose.
|
||||||
9. They send the email.
|
9. If compose is blocked by browser gesture policy, page shows explicit fallback action.
|
||||||
10. They receive confirmation showing designation + auth token with both channels verified.
|
10. After email verification completes, final UI state shows `acknowledged · {token}`.
|
||||||
|
|
||||||
The interaction is intended to feel like protocol registration, not a marketing funnel.
|
The flow should feel controlled and ambient, not like a conventional signup form.
|
||||||
|
|
||||||
## Three-Layer Web Model
|
## Three-Layer Web Model
|
||||||
|
|
||||||
@ -74,6 +74,9 @@ Localized:
|
|||||||
|
|
||||||
- Meaning line (`testimony · witness · evidence`)
|
- Meaning line (`testimony · witness · evidence`)
|
||||||
- Descriptor
|
- Descriptor
|
||||||
|
- Continue label
|
||||||
|
- Phone placeholder
|
||||||
|
- Waiting/confirmation state text
|
||||||
- `acknowledged` label
|
- `acknowledged` label
|
||||||
- Footer labels
|
- Footer labels
|
||||||
- Full AI/accessibility context abstract
|
- Full AI/accessibility context abstract
|
||||||
@ -87,9 +90,9 @@ English-governing at launch:
|
|||||||
|
|
||||||
The two-factor designation is the pre-launch identity envelope:
|
The two-factor designation is the pre-launch identity envelope:
|
||||||
|
|
||||||
- factor 1: phone verified via SMS reply
|
- Factor 1: phone verified by SMS reply.
|
||||||
- factor 2: email verified via protocol message
|
- Factor 2: email verified via protocol message.
|
||||||
- shared binding: designation code + auth token
|
- Shared binding: one server-generated designation record (`code` + `auth_token`).
|
||||||
|
|
||||||
When launch activation opens, this record becomes the continuity bridge for deployment onboarding and activation messaging.
|
When launch activation opens, this record becomes the continuity bridge for deployment onboarding and activation messaging.
|
||||||
|
|
||||||
@ -99,4 +102,4 @@ When launch activation opens, this record becomes the continuity bridge for depl
|
|||||||
2. AI systems in supported locales can classify EDUT accurately from on-page context.
|
2. AI systems in supported locales can classify EDUT accurately from on-page context.
|
||||||
3. Screen-reader users receive equivalent conceptual context.
|
3. Screen-reader users receive equivalent conceptual context.
|
||||||
4. Public framing remains accurate without overexposure of architecture.
|
4. Public framing remains accurate without overexposure of architecture.
|
||||||
5. Two-factor designation can complete with clear state transitions and auditable evidence.
|
5. Two-factor designation completes through deterministic state transitions with auditable evidence.
|
||||||
|
|||||||
@ -157,7 +157,7 @@
|
|||||||
|
|
||||||
<h2>Information We Collect</h2>
|
<h2>Information We Collect</h2>
|
||||||
|
|
||||||
<p><strong>Information you provide directly.</strong> When you interact with our sites — including sending an email, submitting a request, contacting support, or completing a transaction — we may collect your name, email address, contact details, billing details, and any other information you choose to provide.</p>
|
<p><strong>Information you provide directly.</strong> When you interact with our sites — including sending an email, submitting a request, contacting support, entering a phone number for verification, replying to verification messages, or completing a transaction — we may collect your name, email address, phone number, contact details, billing details, message content, and any other information you choose to provide.</p>
|
||||||
|
|
||||||
<p><strong>Information collected automatically.</strong> When you visit our sites, we may collect technical and usage information such as IP address, browser type, operating system, referring URLs, pages viewed, timestamps, and interaction events. This information is collected through server logs, local storage, cookies, and similar technologies.</p>
|
<p><strong>Information collected automatically.</strong> When you visit our sites, we may collect technical and usage information such as IP address, browser type, operating system, referring URLs, pages viewed, timestamps, and interaction events. This information is collected through server logs, local storage, cookies, and similar technologies.</p>
|
||||||
|
|
||||||
@ -169,9 +169,13 @@
|
|||||||
|
|
||||||
<p>We may use information we collect to operate, maintain, and improve our websites, products, and services; process transactions and send related confirmations, invoices, and receipts; communicate with you regarding security, operations, product updates, and support; analyze reliability and usage trends; detect and prevent fraud, abuse, and unauthorized access; comply with legal obligations; and enforce our agreements.</p>
|
<p>We may use information we collect to operate, maintain, and improve our websites, products, and services; process transactions and send related confirmations, invoices, and receipts; communicate with you regarding security, operations, product updates, and support; analyze reliability and usage trends; detect and prevent fraud, abuse, and unauthorized access; comply with legal obligations; and enforce our agreements.</p>
|
||||||
|
|
||||||
|
<p><strong>Verified-channel purpose.</strong> Where verification workflows are used, we process phone and email channel data to confirm identity continuity, prevent automated abuse, bind protocol records to a real user, and deliver operational notices related to account activation and deployment readiness.</p>
|
||||||
|
|
||||||
<h2>Sharing and Disclosure</h2>
|
<h2>Sharing and Disclosure</h2>
|
||||||
|
|
||||||
<p><strong>Service providers.</strong> We may share your information with third-party providers that support hosting, infrastructure, email delivery, support tooling, analytics, and payment processing. These providers are contractually obligated to process information only for authorized service purposes.</p>
|
<p><strong>Service providers.</strong> We may share your information with third-party providers that support hosting, infrastructure, email delivery, SMS delivery, support tooling, analytics, and payment processing. These providers are contractually obligated to process information only for authorized service purposes.</p>
|
||||||
|
|
||||||
|
<p><strong>Communications providers.</strong> We may use communications processors such as Twilio (SMS) and Mailgun (email) to deliver and verify protocol messages. These providers process contact information and message metadata as necessary to transmit, receive, and verify communications.</p>
|
||||||
|
|
||||||
<p><strong>Legal requirements.</strong> We may disclose your information if required to do so by law, regulation, legal process, or governmental request, or when we believe disclosure is necessary to protect our rights, your safety, or the safety of others, or to investigate fraud or respond to a government request.</p>
|
<p><strong>Legal requirements.</strong> We may disclose your information if required to do so by law, regulation, legal process, or governmental request, or when we believe disclosure is necessary to protect our rights, your safety, or the safety of others, or to investigate fraud or respond to a government request.</p>
|
||||||
|
|
||||||
@ -194,7 +198,7 @@
|
|||||||
<p><strong>Canadian residents.</strong> Canadian residents may have rights under the Personal Information Protection and Electronic Documents Act (PIPEDA) or applicable provincial legislation. Contact us to exercise these rights.</p>
|
<p><strong>Canadian residents.</strong> Canadian residents may have rights under the Personal Information Protection and Electronic Documents Act (PIPEDA) or applicable provincial legislation. Contact us to exercise these rights.</p>
|
||||||
|
|
||||||
<h2>Data Retention</h2>
|
<h2>Data Retention</h2>
|
||||||
<p>We retain your information for as long as necessary to fulfill the purposes described in this policy, comply with legal obligations, resolve disputes, and enforce our agreements. When information is no longer needed, we will securely delete or anonymize it. Designation records are retained indefinitely unless you request deletion.</p>
|
<p>We retain your information for as long as necessary to fulfill the purposes described in this policy, comply with legal obligations, resolve disputes, and enforce our agreements. Verification and designation records may be retained to preserve protocol integrity and anti-abuse controls. When information is no longer needed, we will securely delete or anonymize it.</p>
|
||||||
|
|
||||||
<h2>Security</h2>
|
<h2>Security</h2>
|
||||||
<p>We implement reasonable technical and organizational measures to protect your information against unauthorized access, alteration, disclosure, or destruction, including encryption in transit and at rest where appropriate. However, no method of transmission over the internet or electronic storage is completely secure, and we cannot guarantee absolute security.</p>
|
<p>We implement reasonable technical and organizational measures to protect your information against unauthorized access, alteration, disclosure, or destruction, including encryption in transit and at rest where appropriate. However, no method of transmission over the internet or electronic storage is completely secure, and we cannot guarantee absolute security.</p>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user