Skip to content

patriciaeastcott-hash/decoder

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

815 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Text Decoder

A conversation analysis app that helps you decode conversations, understand communication patterns, and build better relationships. Built by Digital ABCs.

What It Does

Paste or type a conversation and Text Decoder uses AI (Google Gemini) to:

  • Split conversations into individual messages with speaker identification
  • Analyse communication patterns and behaviours
  • Provide insights from a curated behaviour library
  • Track conversation history with profiles

Available on iOS (App Store / TestFlight), Android (Google Play), and Web.

Architecture

Layer Technology
Frontend Flutter / Dart (iOS, Android, Web, Desktop)
Backend API Python / Flask on Google Cloud Run
AI Google Gemini via Google Gen AI SDK
Auth Firebase Auth (Google Sign-In, Apple Sign-In)
Hosting Cloud Run (API + web container)
Payments In-App Purchases (Apple/Google)
CI/CD Codemagic (mobile), Cloud Build (API + web)

Firebase Environments

Decoder now uses a strict environment split:

Environment Firebase Project Purpose
Staging text-decoder-app TestFlight builds, shared QA/demo accounts, seeded fake conversation data
Production device-streaming-34aa2596 App Store / released builds and real users only
Local Firebase emulators Safe local development with no live-user impact

Rules:

  • TestFlight uses staging, not production.
  • App Store / released builds use production.
  • Fake/demo seed data belongs in staging only.
  • Production may contain one empty internal QA account for smoke testing, but no demo corpus.

Project Structure

decoder/
├── app.py                  # Flask API backend
├── tests/                  # Python backend tests
├── flutter_app/            # Flutter frontend (iOS/Android/Web/Desktop)
│   ├── lib/               # Dart source code
│   │   ├── screens/       # UI screens (auth, conversation, library, profile)
│   │   ├── services/      # API, auth, storage services
│   │   ├── models/        # Data models
│   │   ├── providers/     # State management (Provider)
│   │   ├── widgets/       # Reusable UI components
│   │   └── utils/         # Utilities
│   └── test/              # Flutter tests
├── data/                   # Behaviour library data
├── scripts/                # Dev workflow scripts
├── firebase.json           # Firebase config + emulators
├── firestore.rules         # Firestore security rules
├── cloudbuild.yaml         # Cloud Build - API deployment
├── cloudbuild-web.yaml     # Cloud Build - Web deployment
├── codemagic.yaml          # Codemagic CI/CD for mobile builds
├── Dockerfile              # API container
└── requirements.txt        # Python dependencies

Getting Started

Backend (Flask API)

# Create virtual environment
python -m venv .venv
source .venv/bin/activate

# Install dependencies
pip install -r requirements.txt

# Run locally
flask run

Frontend (Flutter)

cd flutter_app

# Get dependencies
flutter pub get

# Run on your platform
flutter run \
  --dart-define=APP_ENV=local \
  --dart-define=API_BASE_URL=http://localhost:8080 \
  --dart-define=FIREBASE_WEB_API_KEY=... \
  --dart-define=FIREBASE_WEB_APP_ID=... \
  --dart-define=FIREBASE_WEB_MESSAGING_SENDER_ID=... \
  --dart-define=FIREBASE_WEB_PROJECT_ID=... \
  --dart-define=FIREBASE_WEB_AUTH_DOMAIN=... \
  --dart-define=FIREBASE_WEB_STORAGE_BUCKET=... \
  --dart-define=GOOGLE_WEB_CLIENT_ID=...   # Default device
flutter run -d chrome --dart-define=APP_ENV=local --dart-define=API_BASE_URL=http://localhost:8080
flutter run -d ios --dart-define=APP_ENV=local --dart-define=API_BASE_URL=http://localhost:8080
flutter run -d android --dart-define=APP_ENV=local --dart-define=API_BASE_URL=http://localhost:8080

Running Tests

# All tests
./scripts/test_all.sh

# Backend only
pytest tests/ -v --cov=app

# Flutter only
cd flutter_app && flutter test

Firebase Seeding

Use the guarded admin seeder to create/update Firebase Auth users plus synced Firestore data:

# Staging fake/demo data only
python scripts/seed_firebase.py \
  --env staging \
  --input scripts/seeds/staging_demo.seed.json

# Production internal QA account only
python scripts/seed_firebase.py \
  --env production_qa \
  --input scripts/seeds/production_qa.seed.json

Safety rules:

  • staging seeds can contain fake/demo users and fake conversation data.
  • production_qa seeds require allow_production=true and reject demo data by default.
  • production_qa manifests are limited to a single internal QA account unless you explicitly opt into production conversation seeding.

Audit & Compliance Data

Text Decoder maintains an auditable record of user consent and safety events in Firestore, tied to each user's Firebase Auth UID. This data can be used to demonstrate duty of care and regulatory compliance.

What Is Audited

Record Type Firestore Collection What It Proves
AI Disclaimer Acknowledgement user_consents User confirmed AI analysis is not a replacement for professional psychological, medical, or legal advice
Safety Concern Triggers safety_events User's conversation triggered the compassionate safety response and they were shown emergency service details and helpline numbers
Age Verification user_consents User confirmed they are 16 years of age or older (age_confirmed: true, minimum_age_requirement: 16)

Firestore Collection Schemas

user_consents — One record per user consent event:

{
  uid: "firebase-auth-uid",
  consent_id: "sha256-hash",
  terms_version: "1.0.0",
  terms_accepted: true,
  ai_disclaimer_acknowledged: true,
  age_confirmed: true,
  minimum_age_requirement: 16,
  consent_timestamp: "2026-03-05T10:30:00Z",
  ip_hash: "truncated-sha256",
  user_agent_hash: "truncated-sha256",
  created_at: <server timestamp>
}

safety_events — One record per safety concern detection:

{
  uid: "firebase-auth-uid",
  severity: "moderate" | "severe",
  categories: ["physical violence", "threats to life", ...],
  safety_resources_shown: true,
  emergency_numbers_shown: true,
  timestamp: <server timestamp>
}

No conversation text is stored in safety events (privacy by design). Only the category of concern and severity are recorded.

Querying Audit Data

Firebase Console:

  1. Go to Firestore Database in the Firebase Console
  2. Select the user_consents or safety_events collection
  3. Filter by uid to find records for a specific user

gcloud CLI — All consents for a specific user:

gcloud firestore documents list \
  --collection-group=user_consents \
  --filter="uid=USER_FIREBASE_UID" \
  --project=YOUR_PROJECT_ID

gcloud CLI — All safety events for a specific user:

gcloud firestore documents list \
  --collection-group=safety_events \
  --filter="uid=USER_FIREBASE_UID" \
  --project=YOUR_PROJECT_ID

Cloud Logging (redundant log-based audit trail):

# Consent records
gcloud logging read 'resource.type="cloud_run_revision" AND textPayload:"Consent persisted to Firestore"' \
  --project=YOUR_PROJECT_ID --limit=100 --format=json

# Safety concern detections
gcloud logging read 'resource.type="cloud_run_revision" AND textPayload:"Safety concerns detected"' \
  --project=YOUR_PROJECT_ID --limit=100 --format=json

Privacy Protections

  • IP addresses are stored as truncated SHA-256 hashes (not raw IPs)
  • User agents are stored as truncated SHA-256 hashes
  • Safety events record concern categories only — no conversation text is stored
  • All collections are protected by Firestore security rules: users can only read their own records, writes are restricted to the backend (Firebase Admin SDK)

API Endpoints

Endpoint Method Auth Purpose
/api/v1/legal/consent POST Required Record user consent (persisted to Firestore)
/api/v1/legal/terms GET None Get current terms version and consent requirements
/api/v1/legal/safety-resources GET None Get helpline numbers and emergency service details

Deployment

Platform Method Command
API Cloud Build gcloud builds submit --config=cloudbuild.yaml
Web Cloud Build gcloud builds submit --config=cloudbuild-web.yaml
iOS Codemagic Triggered via Codemagic dashboard or push
Android Codemagic Triggered via Codemagic dashboard or push

Runtime Configuration

API environment variables

The API expects these runtime secrets from Cloud Run / Secret Manager:

Variable Purpose
APP_ENV Deployment environment (staging or production)
FIREBASE_PROJECT_ID Firebase / GCP project the backend must use for the current environment
GEMINI_API_KEY Google Gemini API access
ENCRYPTION_KEY Fernet key for sync encryption
APP_SECRET_KEY Flask session secret
RATE_LIMIT_STORAGE_URI Redis connection for multi-instance rate limiting
CORS_ALLOWED_ORIGINS Semicolon- or comma-separated allowed web origins

Guardrail:

  • API deploys now validate the configured gemini-api-key secret against the Gemini models endpoint before Cloud Run is updated. A Firebase web API key or any other invalid key should fail deploy instead of reaching runtime.

Flutter public build config

Release builds for mobile and web use --dart-define for public client config:

  • APP_ENV
  • API_BASE_URL
  • GOOGLE_WEB_CLIENT_ID
  • GOOGLE_IOS_CLIENT_ID
  • APPLE_SERVICE_ID
  • FIREBASE_PROJECT_ID
  • FIREBASE_WEB_API_KEY
  • FIREBASE_WEB_APP_ID
  • FIREBASE_WEB_MESSAGING_SENDER_ID
  • FIREBASE_WEB_PROJECT_ID
  • FIREBASE_WEB_AUTH_DOMAIN
  • FIREBASE_WEB_STORAGE_BUCKET
  • FIREBASE_WEB_MEASUREMENT_ID

Do not ship backend-only secrets in Flutter builds.

Important distinction:

  • FIREBASE_WEB_API_KEY is public client configuration and will appear in built web/mobile clients.
  • GEMINI_API_KEY, APP_SECRET_KEY, ENCRYPTION_KEY, and RATE_LIMIT_STORAGE_URI are backend-only secrets and must never be passed to Flutter builds or bundled into client artifacts.

Native Firebase files are environment-specific:

  • staging iOS / Android configs must target text-decoder-app
  • production iOS / Android configs must target device-streaming-34aa2596
  • use flutter_app/scripts/install_firebase_config.py in CI to install the correct native config before building

Distribution

  • iOS staging: TestFlight via ios-testflight-staging
  • iOS production: App Store via ios-release-production
  • Android: Firebase App Distribution or Google Play
  • Web: Cloud Run static web container

Deployment helpers:

  • scripts/deploy_api_staging.sh
  • scripts/deploy_api_production.sh
  • scripts/deploy_web_staging.sh
  • scripts/deploy_web_production.sh

Security helpers:

  • python scripts/validate_gemini_secret.py --project text-decoder-app
  • python scripts/validate_gemini_secret.py --project device-streaming-34aa2596
  • python scripts/audit_client_artifacts.py --path flutter_app/build/web

See archived/TESTER_DISTRIBUTION.md for detailed tester onboarding instructions.