Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ff7cf44
chore: pin upstream SHA + DRIFT.md (Believe self-host fork)
Jbelieve Jun 12, 2026
29ef031
feat(group-a): Postgres schema (54 tablas omi_*) + GCS→R2 swap
Jbelieve Jun 12, 2026
4b27c2c
feat(group-b1): repository foundations + auth JWT/Argon2id + pgvector…
Jbelieve Jun 12, 2026
900aafd
feat(group-b2): portar los 34 módulos database/ restantes a Postgres
Jbelieve Jun 13, 2026
8e67e22
feat(fanout): Omi conversations → Believe Company Brain (HMAC firmado)
Jbelieve Jun 13, 2026
84ca20a
build(deploy): Dockerfile.believe con base pública python:3.11-slim-b…
Jbelieve Jun 13, 2026
5b73cca
build(deploy): compose Believe para Contabo (REDIS_DB_* corregido, im…
Jbelieve Jun 13, 2026
80dfdf3
fix(boot): translate client lazy + compose env_file
Jbelieve Jun 13, 2026
356f04c
docs: DRIFT.md estado deploy vivo 2026-06-13
Jbelieve Jun 13, 2026
0f08e4e
feat(desktop): login email/password JWT propio en la app macOS
Jbelieve Jun 13, 2026
d4dd50c
fix(fanout): cliente httpx propio en push_conversation_to_brain
Jbelieve Jun 13, 2026
7107b10
feat(windows): auth JWT propio Believe reemplaza Firebase
Jbelieve Jun 13, 2026
6ada029
feat(windows): pantalla login email/password + registro
Jbelieve Jun 13, 2026
ef9109f
refactor(windows): displayName local en vez de Firebase updateProfile
Jbelieve Jun 13, 2026
c35435d
refactor(windows): tipo User desde lib/firebase en useAuth
Jbelieve Jun 13, 2026
a539342
refactor(windows): tipo User desde lib/firebase en Sidebar
Jbelieve Jun 13, 2026
24ae3f1
refactor(windows): tipo User desde lib/firebase en Home
Jbelieve Jun 13, 2026
1b6ebbb
build(windows): .env.example apunta al backend self-host Believe
Jbelieve Jun 13, 2026
72b74e0
ci(windows): workflow GitHub Actions que buildea el .exe
Jbelieve Jun 13, 2026
92d6d05
feat(macos): script firma Developer ID + notarizacion + DMG
Jbelieve Jun 13, 2026
0c5d62a
docs(macos): guia distribucion notarizada (cert Developer ID + notary…
Jbelieve Jun 13, 2026
38c76fa
feat: conectar Langfuse al get_llm() central via LangChain CallbackHa…
Jbelieve Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/windows_build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Builds the Windows desktop app (.exe installer) on a Windows runner.
#
# Cross-building from macOS is unreliable (better-sqlite3 + koffi native modules
# and the PowerShell OCR helper need a real Windows toolchain), so the installer
# is produced here. Manual trigger by default; also fires on a v*-windows tag.
#
# The .exe ships UNSIGNED (no Windows code-signing cert yet) — Windows SmartScreen
# shows a one-time "unknown publisher" prompt the team can accept. To sign later,
# add CSC_LINK + CSC_KEY_PASSWORD secrets and electron-builder picks them up.
name: Windows desktop build

on:
workflow_dispatch: {}
push:
tags:
- 'v*-windows'

defaults:
run:
working-directory: desktop/windows

jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10

- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
cache-dependency-path: desktop/windows/pnpm-lock.yaml

# The renderer reads VITE_* at build time. VITE_OMI_API_BASE is the only
# value that must point at the self-host backend; it is public, not a secret.
# PostHog keys have baked-in defaults. Override any of these via repo secrets
# if needed (a secret wins over this default).
- name: Write .env
shell: bash
run: |
echo "VITE_OMI_API_BASE=${VITE_OMI_API_BASE:-https://omi.believe-global.com}" >> .env
echo "VITE_OMI_DESKTOP_API_BASE=${VITE_OMI_DESKTOP_API_BASE:-https://desktop-backend-hhibjajaja-uc.a.run.app}" >> .env
echo "VITE_ENABLE_GOOGLE_INTEGRATION=0" >> .env
env:
VITE_OMI_API_BASE: ${{ secrets.VITE_OMI_API_BASE }}
VITE_OMI_DESKTOP_API_BASE: ${{ secrets.VITE_OMI_DESKTOP_API_BASE }}

# Full install (scripts ON): compiles better-sqlite3 for win32 via
# electron-rebuild and builds the OCR helper through the PowerShell script.
# --no-frozen-lockfile: the committed lockfile drifts slightly across pnpm
# versions; don't fail the build on that (it's not a dependency change).
- name: Install dependencies
run: pnpm install --no-frozen-lockfile

- name: Build installer
run: pnpm run build:win

- name: Upload installer
uses: actions/upload-artifact@v4
with:
name: omi-windows-installer
path: desktop/windows/dist/*.exe
if-no-files-found: error
retention-days: 30
52 changes: 52 additions & 0 deletions DRIFT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# DRIFT.md — Fork Believe de BasedHardware/omi

## Pin

- **Upstream:** `BasedHardware/omi` (MIT)
- **Fork:** `Jbelieve/omi`
- **SHA congelado:** `4e4e548b56d9bc57ca92570aa62b58b59d3a2289` (upstream main, 2026-06-13 00:11 +0530)
- **Regla:** NUNCA `git pull` de upstream a mitad de un build. Rebases solo como decisión explícita con re-test completo.

## Swap implementado (rama `believe/self-host`)

| Upstream | Believe | Estado |
|---|---|---|
| Firestore (`database/_client.py`) | Postgres 16 + asyncpg, tablas `omi_*` | en curso |
| Pinecone (`database/vector_db.py`) | pgvector 768d HNSW (+halfvec 3072d screen-activity) | en curso |
| GCS (`utils/other/storage.py`) | Cloudflare R2 single-bucket, prefijos `audio/ backups/ speech-profiles/ plugin-logos/` | en curso |
| Firebase Auth | JWT propio HS256 + Argon2id (t=3, m=64MiB, p=4) + refresh tokens | en curso |
| Embeddings text-embedding-3-large 3072d | Ollama `nomic-embed-text` 768d (believe-ollama, Contabo) | en curso |
| Webhook fanout sin firma | `X-Omi-Signature: t=<ts>,v1=<hmac>` con `OMI_WEBHOOK_SHARED_SECRET` | en curso |

## Deudas técnicas aceptadas (no bloquean DONE)

1. **Rotación de `ENCRYPTION_SECRET`**: hoy imposible sin full re-encrypt. Wave 9: columna `key_version` en conversations/memories.
2. **LangSmith → Langfuse**: `LANGSMITH_*` vacíos por ahora; instrumentar Langfuse (langfuse.believe-global.com) en Wave 9.
3. **STT sigue siendo SaaS (Deepgram)**: streaming STT requiere `DEEPGRAM_API_KEY`. Alternativa self-host pura (Parakeet/Whisper) detrás de la ABC `PrerecordedSTTProvider` queda para después.
4. **FCM push**: `firebase_admin` queda solo para tokens FCM si la app lo pide; ruta alternativa Novu webpush-relay (self-hosted) en parking.
5. **ADMIN_KEY prefix-bypass**: mitigado con audit-log; reemplazo por RBAC real en Wave 9.

## Divergencias del upstream detectadas post-recon (2026-06-12)

- Upstream agregó `backend/typesense/`, `backend/diarizer/`, colección action_items separada y backfill de memory vectors (`backend/migrations/00X_*.py` son data-migrations de Firestore, no SQL).
- El recon (`recon/omi.md`, 2026-06-10) sigue siendo válido en estructura: `_client.py` Firestore global, 40 archivos database/, requirements.txt pinneado.

## Fixes de boot descubiertos en el deploy vivo (2026-06-13)

El upstream construye varios clientes a NIVEL MÓDULO (eager), que exigen sus env vars en el import y rompían el boot. Resueltos:
- **`utils/translation.py`**: `TranslationServiceClient()` eager → hecho lazy (`_LazyTranslateClient`). Translation usa Google Cloud Translate (no self-hosteado) y está feature-gated por `users/{uid}.language`. Fix en el fork.
- **`utils/conversations/search.py`**: `typesense.Client()` eager exige `TYPESENSE_API_KEY`. Typesense NO está desplegado en Believe → placeholder `disabled-not-deployed` en `.env`. Search por keyword degradado (no en el path crítico). DEUDA: desplegar Typesense o hacer el cliente lazy.
- **`utils/other/hume.py`**: `HumeClient()` eager → placeholder. Emotion detection deshabilitado.
- **`utils/llm/clients.py:619` + `fair_use_classifier.py` + `chat_file.py`**: `ChatOpenAI`/`AsyncOpenAI` eager exigen `OPENAI_API_KEY` presente al boot. NO es fail-soft. `OPENAI_API_KEY` es dependencia DURA de boot (placeholder en el deploy inicial; pendiente la key real de Jorge).
- **`docker-compose.yml`**: agregado `env_file: .env` — el bloque `environment:` solo pasa las vars listadas; sin env_file los placeholders de Typesense/Hume no llegaban al container.

## Estado del deploy (2026-06-13)

- **VIVO**: `https://omi.believe-global.com/health` → 200, cert Let's Encrypt válido (hasta 2026-09-11). Containers `omi-backend` + `omi-postgres` (pgvector/pg16) + `omi-redis` healthy en Contabo. 54 tablas `omi_*` aplicadas via initdb. Imagen `omi-backend:believe` (3.62GB) buildeada en Contabo con `Dockerfile.believe` (base pública).
- **Auth E2E verde en vivo**: register → login → request autenticado con JWT propio. Usuario persiste en `omi_users`, refresh tokens en `omi_refresh_tokens`.
- **Pendiente de Jorge para el loop audio→getmaas 100%** (2 secrets + 1 físico):
1. `OPENAI_API_KEY` real (hoy placeholder — hard boot dep, gpt-4.1-mini estructura conversaciones).
2. `BRAIN_INGEST_TOKEN` (hoy vacío — el fanout firma pero no empuja sin él; mismo token mc_* del worker gmail/bechat).
3. `R2_*` reales en el `.env` del server (hoy vacíos — audio chunk storage). Valores existen en la infra Believe; el guardrail bloqueó auto-wirearlos.
4. Stream de audio real (device Omi o WS simulado) para el smoke físico completo.
- DNS `omi.believe-global.com` → 217.216.49.241 creado por Jorge (Cloudflare grey-cloud).
26 changes: 26 additions & 0 deletions app/.env.believe
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# app/.env — Believe self-host PRODUCTION
#
# Usage:
# cd app
# cp .env.believe .env
# flutter clean
# dart run build_runner build --delete-conflicting-outputs
#
# build_runner reads this file via `envied` (lib/env/prod_env.dart / dev_env.dart)
# and emits *_env.g.dart with API_BASE_URL obfuscated + embedded in the binary.
#
# NOTE: API_BASE_URL has NO trailing slash. The HTTP layer concatenates paths
# that already start with "/", so a trailing slash would double up.

OPENAI_API_KEY=
API_BASE_URL=https://omi.believe-global.com
GOOGLE_MAPS_API_KEY=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
USE_WEB_AUTH=false
# DR #10: the app speaks the backend's own JWT (POST /v1/auth/login, email+password).
# NO Firebase Auth, NO token exchange. FIREBASE_GRACE off. Clean re-login at cutover.
USE_AUTH_CUSTOM_TOKEN=true
# Empty in prod. For internal dev builds use .env.believe.dev (sets STAGING_API_URL too).
STAGING_API_URL=
POSTHOG_API_KEY=
20 changes: 20 additions & 0 deletions app/.env.believe.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# app/.dev.env — Believe self-host, internal dev builds
#
# Usage:
# cd app
# cp .env.believe.dev .dev.env
# flutter clean
# dart run build_runner build --delete-conflicting-outputs
#
# Both URLs point at the self-host backend so the in-app staging<->prod switch
# (Settings) lands on the same Believe backend for QA.

OPENAI_API_KEY=
API_BASE_URL=https://omi.believe-global.com
GOOGLE_MAPS_API_KEY=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
USE_WEB_AUTH=false
USE_AUTH_CUSTOM_TOKEN=true
STAGING_API_URL=https://omi.believe-global.com
POSTHOG_API_KEY=
6 changes: 6 additions & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,9 @@ whisper_flutter_new/

# Maestro test reports (generated)
.maestro/reports/

# Believe self-host: real envied secret files stay ignored (root .gitignore: *.env / .env*),
# but the no-secret Believe templates are tracked so the team can `cp` them into .env.
# (They contain only the public backend URL + non-secret flags.)
!.env.believe
!.env.believe.dev
152 changes: 152 additions & 0 deletions app/BELIEVE_BUILD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Omi (Believe) — Build & Distribution

Self-host fork (`believe/self-host`). Backend: `https://omi.believe-global.com`.
Bundle ID (DR #12): **`com.believe.omi`** on both Android and iOS.

> Android APK is the **minimum deliverable** of area 7-apps. iOS is gated behind
> Believe Apple Developer credentials (human dependency) — see §iOS.

---

## 0. One-time: install the Flutter toolchain

This machine currently has **no Flutter/Dart** (`which flutter` → not found). Install:

```bash
# macOS, recommended
brew install --cask flutter # or: https://docs.flutter.dev/get-started/install
flutter --version # expect 3.x stable
flutter doctor # accept Android licenses: flutter doctor --android-licenses
```

The repo also needs the Android SDK + a JDK 21 (build.gradle pins
`sourceCompatibility = VERSION_21`).

---

## 1. Point the app at the Believe backend (envied build-time)

The backend URL is compiled into the binary (obfuscated) by `envied`. There is no
runtime URL in `Info.plist` / `AndroidManifest.xml`.

```bash
cd app
cp .env.believe .env # API_BASE_URL=https://omi.believe-global.com, USE_AUTH_CUSTOM_TOKEN=true
flutter pub get
flutter clean
dart run build_runner build --delete-conflicting-outputs # regenerates prod_env.g.dart / dev_env.g.dart
```

For internal dev builds (in-app staging<->prod switch lands on the same backend):
`cp .env.believe.dev .dev.env` instead.

Verify the value got embedded:
```bash
grep -c "omi.believe-global.com" lib/env/prod_env.g.dart # > 0 after build_runner (value is obfuscated as byte array — grep may not match; instead print at boot)
```
At boot, `Env.apiBaseUrl` must return `https://omi.believe-global.com`.

---

## 2. Android — signed release APK (the deliverable)

### 2.1 Create the release keystore (once)

**Never commit the keystore.** `android/.gitignore` already ignores
`key.properties`, `**/*.jks`, `**/*.keystore`.

```bash
cd app/android
keytool -genkey -v \
-keystore upload-keystore.jks \
-keyalg RSA -keysize 2048 -validity 10000 \
-alias upload \
-storetype JKS
# It prompts for: store password, key password, CN/OU/O (use: Believe).
```

Then create `app/android/key.properties` (also git-ignored) pointing gradle at it:
```properties
storePassword=<store password you just set>
keyPassword=<key password you just set>
keyAlias=upload
storeFile=upload-keystore.jks
```

`build.gradle` reads `rootProject.file('key.properties')` and uses
`signingConfigs.release` for the **prod** flavor (dev uses debug signing).

**Persist the keystore in Infisical so every build signs identically** (required —
Android refuses to install an update signed by a different key):
```bash
base64 -i app/android/upload-keystore.jks | pbcopy # -> Infisical believe-infra/prod : OMI_ANDROID_KEYSTORE_BASE64
# also store OMI_ANDROID_KEYSTORE_PASSWORD, OMI_ANDROID_KEY_PASSWORD, OMI_ANDROID_KEY_ALIAS=upload
```
To restore on a fresh machine:
```bash
~/.believe/bin/infra bash -c 'echo "$OMI_ANDROID_KEYSTORE_BASE64" | base64 -d > app/android/upload-keystore.jks'
```

### 2.2 Build

> NOTE: this fork has **no `lib/main_prod.dart`** — the single entrypoint is
> `lib/main.dart` and the flavor is selected with `--flavor prod`
> (`flavors.dart` reads `appFlavor`). The stale `release.sh` references
> `-t lib/main_prod.dart` which does not exist here. Use the command below.

```bash
cd app
flutter build apk --release --flavor prod
# output: build/app/outputs/flutter-apk/app-prod-release.apk
```

(App Bundle for Play Store, if ever needed: `flutter build appbundle --release --flavor prod`.)

### 2.3 Upload to Cloudflare R2 (`believe-r2`, prefix `app-distribution/`)

R2 credentials live in Infisical (`R2_ACCESS_KEY`, `R2_SECRET_KEY`, `R2_ENDPOINT`, bucket `believe-r2`).

```bash
~/.believe/bin/infra bash -c '
AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY" \
AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY" \
aws s3 cp app/build/app/outputs/flutter-apk/app-prod-release.apk \
s3://believe-r2/app-distribution/omi/omi-$(date +%Y%m%d).apk \
--endpoint-url "$R2_ENDPOINT" \
--content-type application/vnd.android.package-archive
'
```

Then share the link (or pin in Be Chat `#dev`). Users enable
"Install from Unknown Sources" and download. Same keystore on every build → updates
install over the previous version.

---

## 3. iOS — TestFlight (gated: Believe Apple Developer credentials, human dependency)

Bundle ID is already set to `com.believe.omi` in `ios/Flutter/Base.xcconfig`
(`APP_BUNDLE_IDENTIFIER`). What still requires Apple credentials (not done here):

1. Create App ID `com.believe.omi` in the Believe Apple Developer account (NEW, do not reuse).
2. Provisioning profile + signing certificate; create the app in App Store Connect.
3. Build + upload:
```bash
cd app && cp .env.believe .env
flutter clean && dart run build_runner build --delete-conflicting-outputs
flutter build ipa --release --flavor prod
xcrun altool --upload-app -f build/ios/ipa/*.ipa -u <apple-id> -p <app-specific-password>
```
4. TestFlight → group "Believe Internal" → invite the @believe-global.com emails.

Without Apple credentials this section does not run; the APK (§2) is the deliverable.

---

## 4. Login (DR #10 — backend JWT, no Firebase Auth)

The Believe build uses the backend's own JWT (email+password) via
`lib/services/believe_auth_service.dart` (`POST /v1/auth/{register,login,refresh,logout}`).
See that file + the report for which screens are wired vs. pending.

`USE_AUTH_CUSTOM_TOKEN=true` and `USE_WEB_AUTH=false` in `.env.believe`.
8 changes: 4 additions & 4 deletions app/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ android {
productFlavors {
prod {
dimension "flavor-type"
applicationId "com.friend.ios"
resValue "string", "app_name", "Omi"
applicationId "com.believe.omi" // DR #12 — Believe self-host bundle ID
resValue "string", "app_name", "Omi (Believe)"
}
dev {
dimension "flavor-type"
Expand Down Expand Up @@ -89,8 +89,8 @@ android {
}

defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.friend.ios"
// DR #12 — Believe self-host bundle ID (prod flavor overrides this with the same value).
applicationId "com.believe.omi"
minSdkVersion 29
targetSdkVersion 35
versionCode flutterVersionCode.toInteger()
Expand Down
6 changes: 3 additions & 3 deletions app/flavorizr.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
flavors:
prod:
app:
name: "Omi"
name: "Omi (Believe)"
icon: "assets/images/app_launcher_icon.png"
android:
applicationId: "com.friend.ios"
applicationId: "com.believe.omi" # DR #12 — Believe self-host
icon: "assets/images/app_launcher_icon.png"
firebase:
config: "app/android/app/src/prod/google-services.json"
ios:
bundleId: "com.friend-app-with-wearable.ios12"
bundleId: "com.believe.omi" # DR #12 — App ID nuevo en Believe Developer Account
icon: "assets/images/app_launcher_icon.png"
firebase:
config: "app/ios/Config/Prod/GoogleService-Info.plist"
Expand Down
3 changes: 2 additions & 1 deletion app/ios/Flutter/Base.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
APP_BUNDLE_IDENTIFIER=com.friend-app-with-wearable.ios12
APP_BUNDLE_IDENTIFIER=com.believe.omi

6 changes: 6 additions & 0 deletions app/lib/backend/preferences.dart
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,12 @@ class SharedPreferencesUtil {

set tokenExpirationTime(int value) => saveInt('tokenExpirationTime', value);

// Believe self-host (DR #10): backend JWT refresh token. Empty when not using
// the Believe JWT auth path (Firebase builds leave this untouched).
String get refreshToken => getString('refreshToken');

set refreshToken(String value) => saveString('refreshToken', value);

String get email => getString('email');

set email(String value) => saveString('email', value);
Expand Down
Loading
Loading