Skip to content

@ucca/vmc — Verified Media Capture

Engineering Brief v1.0

Classification: Reusable Platform Module

Author: UCCA Platform / Tim Rignold

Date: 2026-03-08


1. PURPOSE

@ucca/vmc is a standalone, reusable module for capturing verified voice and video messages tied to a cryptographically provable identity gate.

It is NOT a contact form feature. It is a platform primitive.

The module captures WHO recorded something, WHAT they recorded, WHEN they recorded it, FROM WHAT device and context — and delivers the complete provenance manifest alongside the media artifact to any configured destination.

First consumer: ucca.online GET IN TOUCH modal. Future consumers: RTOpacks onboarding, ops console, any UCCA world deployment.


2. PACKAGE LOCATION

ucca-engine/
└── packages/
    └── vmc/
        ├── package.json
        ├── README.md
        ├── src/
        │   ├── index.ts              — public API
        │   ├── otp/
        │   │   ├── send.ts           — Twilio OTP dispatch
        │   │   └── verify.ts         — OTP verification
        │   ├── capture/
        │   │   ├── recorder.ts       — MediaRecorder wrapper
        │   │   ├── voice.ts          — voice capture config
        │   │   └── video.ts          — video capture config
        │   ├── upload/
        │   │   └── r2.ts             — R2 presigned upload
        │   ├── notify/
        │   │   └── resend.ts         — Resend notification
        │   ├── manifest/
        │   │   └── build.ts          — provenance manifest builder
        │   ├── ratelimit/
        │   │   └── kv.ts             — KV rate limiting
        │   └── types.ts              — all TypeScript interfaces
        └── tests/
            └── manifest.test.ts

3. PUBLIC API

The module exposes a single clean interface. Consumers pass config. The module handles everything.

import { createVMC } from '@ucca/vmc'

const vmc = createVMC({
  // Identity gate
  otp: {
    twilioAccountSid: env.TWILIO_ACCOUNT_SID,
    twilioAuthToken: env.TWILIO_AUTH_TOKEN,
    fromNumber: env.TWILIO_FROM_NUMBER,    // configurable per deployment
  },

  // Storage
  r2: {
    accountId: env.CF_ACCOUNT_ID,
    bucketName: env.VMC_R2_BUCKET,         // configurable per deployment
    accessKeyId: env.R2_ACCESS_KEY_ID,
    secretAccessKey: env.R2_SECRET_ACCESS_KEY,
    presignExpirySeconds: 172800,          // 48 hours default
  },

  // Notification
  resend: {
    apiKey: env.RESEND_API_KEY,
    from: env.VMC_FROM_EMAIL,              // configurable per deployment
    to: env.VMC_TO_EMAIL,                  // configurable per deployment
  },

  // Rate limiting
  rateLimit: {
    kvNamespace: env.VMC_KV_NAMESPACE,
    maxPerNumber: 1,                        // submissions per window
    windowHours: 24,                        // rolling window
  },

  // Capture config
  capture: {
    maxVoiceSeconds: 120,                   // 2 minutes
    maxVideoSeconds: 90,                    // 90 seconds
    allowVideo: true,                       // can disable per deployment
  },

  // Context — injected by consumer, appears in manifest
  context: {
    surface: 'ucca.online',                // which surface deployed on
    deploymentId: 'ucca-marketing-v1',
  },
})

4. THE FLOW

1. GATE
   User enters mobile number
   → VMC calls Twilio, sends 6-digit OTP via SMS
   → User enters OTP
   → VMC verifies
   → On success: session token issued (short-lived, stored in memory)
   → On failure: 3 attempts max, then 1 hour lockout

2. RATE LIMIT CHECK
   → VMC checks KV: has this number submitted in the last 24 hours?
   → If yes: "You've already sent a message.
              You can send another after [timestamp]."
   → If no: proceed

3. CAPTURE MODE SELECTION
   User chooses:
   → // TYPE A MESSAGE     (text — always available)
   → // LEAVE A VOICE MESSAGE   (audio — mic required)
   → // LEAVE A VIDEO MESSAGE   (video — camera+mic,
                                  shown only if device supports it)

   Device capability detection:
   → Check navigator.mediaDevices.getUserMedia support
   → Check camera availability for video option
   → Graceful degradation — if no mic, voice option hidden
   → If no camera, video option hidden

4. RECORDING
   Voice:
   → Hold-to-record OR tap start/stop
   → Live waveform visualiser (ambient, not distracting)
   → Countdown timer visible
   → Auto-stops at maxVoiceSeconds

   Video:
   → Live preview in modal
   → Tap to start/stop
   → Countdown timer visible
   → Auto-stops at maxVideoSeconds

   Text:
   → Simple textarea
   → 1000 char limit
   → No OTP required for text — gate is voice/video only
     (text is low abuse risk, no storage cost)

5. PREVIEW
   → User can play back before sending
   → "Send" or "Re-record" options
   → No editing — what you recorded is what you send

6. UPLOAD
   → Blob uploaded directly to R2 via presigned URL
   → Upload progress indicator
   → File naming convention:
     vmc/{surface}/{YYYY-MM-DD}/{phone-hash}/{uuid}.webm

   Note: phone number is HASHED in the file path (privacy)
   but stored in full in the manifest.

7. MANIFEST BUILD
   → On successful upload, manifest is built (see Section 5)

8. NOTIFICATION
   → Resend fires to configured recipient
   → Email contains full manifest + R2 presigned link
   → Link expires in 48 hours

9. CONFIRMATION
   → User sees: "Received. We'll be in touch."
   → Modal closes
   → No further detail — clean exit

5. PROVENANCE MANIFEST

Every submission generates a manifest. This is the provenance record. Stored in R2 alongside the media file as {uuid}.manifest.json. Also delivered in full in the Resend notification email.

{
  "manifest_version": "1.0",
  "uuid": "a3f7c291-...",
  "submitted_at_utc": "2026-03-08T08:45:16Z",

  "identity": {
    "phone_verified": true,
    "phone_number": "+61422334489",
    "phone_hash": "sha256:a3f9...",
    "otp_verified_at_utc": "2026-03-08T08:44:52Z",
    "verification_method": "sms-otp"
  },

  "media": {
    "type": "voice",
    "duration_seconds": 103,
    "size_bytes": 824320,
    "mime_type": "audio/webm",
    "r2_key": "vmc/ucca.online/2026-03-08/a3f9.../a3f7c291.webm",
    "r2_presign_url": "https://...",
    "r2_presign_expires_utc": "2026-03-10T08:45:16Z"
  },

  "device": {
    "user_agent": "Mozilla/5.0 ...",
    "platform": "iPhone",
    "os": "iOS 17.4",
    "browser": "Safari",
    "viewport_width": 390,
    "viewport_height": 844,
    "media_recorder_mime": "audio/webm;codecs=opus"
  },

  "context": {
    "surface": "ucca.online",
    "deployment_id": "ucca-marketing-v1",
    "page_language": "ja",
    "page_url": "https://ucca.online",
    "referrer": "https://google.com",
    "timezone": "Asia/Tokyo",
    "local_time": "2026-03-08T17:45:16+09:00"
  },

  "rate_limit": {
    "number_hash": "sha256:a3f9...",
    "submissions_in_window": 1,
    "window_resets_utc": "2026-03-09T08:44:52Z"
  }
}

6. R2 BUCKET STRUCTURE

vmc/
└── {surface}/
    └── {YYYY-MM-DD}/
        └── {phone-hash-prefix}/
            ├── {uuid}.webm          — media file
            └── {uuid}.manifest.json — provenance manifest

Bucket: ucca-vmc (new dedicated bucket, separate from terraform state) Lifecycle rule: auto-delete after 90 days (configurable) Public access: NONE. Presigned URLs only.


7. RESEND NOTIFICATION TEMPLATE

Subject: VMC // {type} // {surface} // {duration} // {local_time}

Example: VMC // VOICE // ucca.online // 1m 43s // Tokyo 17:45

Body (plain text + HTML):

VERIFIED MEDIA CAPTURE
──────────────────────
Surface:    ucca.online
Type:       Voice — 1m 43s
Received:   2026-03-08 08:45:16 UTC
            (Tokyo: 17:45:16 JST)

IDENTITY
──────────────────────
Phone:      +61 422 334 489 (verified)
OTP at:     08:44:52 UTC

CONTEXT
──────────────────────
Language:   Japanese (ja)
Device:     iPhone / iOS 17 / Safari
Referrer:   google.com
Timezone:   Asia/Tokyo

MEDIA
──────────────────────
[PLAY RECORDING]
https://r2-presigned-url...
Link expires: 2026-03-10 08:45 UTC

[VIEW MANIFEST]
https://r2-presigned-manifest-url...

──────────────────────
@ucca/vmc v1.0

8. RATE LIMITING — KV SCHEMA

Key:   vmc:ratelimit:{phone_hash}
Value: {
         "count": 1,
         "first_at": "2026-03-08T08:44:52Z",
         "last_at": "2026-03-08T08:44:52Z",
         "window_expires": "2026-03-09T08:44:52Z"
       }
TTL:   86400 seconds (24 hours, rolling)

KV Namespace: VMC_RATELIMIT (new namespace, separate from REG_INTEL)

Blacklist:

Key:   vmc:blacklist:{phone_hash}
Value: { "reason": "abuse", "added_at": "..." }
TTL:   none (permanent until manually removed)


9. ABUSE PREVENTION SUMMARY

Layer Mechanism Coverage
1 Phone OTP verification Requires real mobile number
2 1 submission per number per 24hr KV rate limit
3 3 OTP attempts max then 1hr lockout KV lockout
4 50MB hard upload size cap R2 presign config
5 audio/webm + video/webm only MIME type whitelist
6 Phone blacklist KV permanent block
7 Text submissions ungated Low risk, no storage cost

10. DEVICE CAPABILITY DETECTION

const capabilities = await detectCapabilities()
// Returns:
{
  hasMicrophone: boolean,
  hasCamera: boolean,
  supportsMediaRecorder: boolean,
  supportedMimeTypes: string[],   // e.g. ['audio/webm;codecs=opus']
  isMobile: boolean,
  isSecureContext: boolean,        // must be true for getUserMedia
}

Rules:

  • Voice option shown only if: hasMicrophone && supportsMediaRecorder && isSecureContext
  • Video option shown only if: hasCamera && hasMicrophone && supportsMediaRecorder && isSecureContext
  • Text always shown
  • If only text available: no mention of voice/video — modal just shows the form

11. FIRST CONSUMER — ucca.online GET IN TOUCH

// apps/marketing/app/components/ContactModal.tsx

import { VMCModal } from '@ucca/vmc/react'

<VMCModal
  config={vmcConfig}
  trigger={<button>GET IN TOUCH</button>}
  theme="ucca-dark"          // dark theme, green accents, IBM Plex
  strings={{
    title: t.contact_title,
    subtitle: t.contact_subtitle,
    confirmed: t.contact_confirmed,
  }}
/>

The VMC React component accepts a theme prop and a strings prop for i18n. All UI strings passed in from the consumer's translation system. The module itself has no hardcoded user-facing strings.


12. ENVIRONMENT VARIABLES REQUIRED

# Twilio
TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
VMC_TWILIO_FROM_NUMBER=       # which number to send OTP from

# R2
CF_ACCOUNT_ID=                # already exists
R2_ACCESS_KEY_ID=             # already exists (or create VMC-specific)
R2_SECRET_ACCESS_KEY=         # already exists (or create VMC-specific)
VMC_R2_BUCKET=ucca-vmc        # new bucket

# Resend
RESEND_API_KEY=               # already exists
VMC_FROM_EMAIL=               # e.g. noreply@ucca.online
VMC_TO_EMAIL=                 # e.g. tim@ucca.online

# KV
VMC_KV_NAMESPACE_ID=          # new KV namespace: VMC_RATELIMIT

13. CLOUDFLARE INFRASTRUCTURE REQUIRED

New resources — add to Terraform:

# New R2 bucket
resource "cloudflare_r2_bucket" "vmc" {
  account_id = var.cloudflare_account_id
  name       = "ucca-vmc"
}

# New KV namespace
resource "cloudflare_workers_kv_namespace" "vmc_ratelimit" {
  account_id = var.cloudflare_account_id
  title      = "VMC_RATELIMIT"
}

New Worker required: vmc-api.ucca.online Handles: OTP send/verify, R2 presign generation, manifest storage, Resend dispatch The browser never touches R2 directly — all via the Worker.


14. NEW I18N KEYS REQUIRED

Add to translations.ts for all locales:

contact_title
contact_subtitle
contact_type_text
contact_type_voice
contact_type_video
contact_phone_prompt
contact_otp_prompt
contact_otp_error
contact_record_instruction_voice
contact_record_instruction_video
contact_preview_send
contact_preview_rerecord
contact_sending
contact_confirmed
contact_ratelimit_message
contact_error_no_mic
contact_error_no_camera
contact_error_upload

15. BUILD ORDER

Alex should build in this sequence:

1. Create packages/vmc/ skeleton — types.ts, index.ts
2. Terraform — R2 bucket + KV namespace
3. vmc-api Worker — OTP send/verify endpoints
4. R2 presign endpoint in Worker
5. Resend notification in Worker
6. KV rate limiting in Worker
7. packages/vmc/src — client-side capture logic
8. packages/vmc/react — VMCModal React component
9. Wire into ucca.online ContactModal
10. Test end-to-end: OTP → record → upload → manifest → email
11. Deploy vmc-api.ucca.online
12. Deploy ucca.online with VMC wired in

16. SURFACE RULE

SURFACE: ucca-engine (packages/vmc/ + new Worker)
         ucca.online (apps/marketing — consumer only)
         ucca-infra (Terraform — R2 + KV)

DO NOT touch: ops console, time.ucca.online,
              any other surface

17. COMMIT CONVENTION

feat(vmc): scaffold package structure and types
feat(vmc): terraform R2 bucket and KV namespace
feat(vmc): OTP send/verify worker endpoints
feat(vmc): R2 presign and manifest storage
feat(vmc): resend notification template
feat(vmc): KV rate limiting and blacklist
feat(vmc): client MediaRecorder capture module
feat(vmc): VMCModal React component
feat(vmc): wire into ucca.online contact modal
feat(vmc): end-to-end test and deploy

@ucca/vmc v1.0 — Verified Media Capture Build once. Deploy everywhere. Who recorded it. What they recorded. When. Immutable.


Version History

Version Date Change Author
1.0 2026-03-08 Initial creation from engineering brief Tim Rignold