How Thari Works
Under the Hood
The Architecture
Three independent layers. You own the first two. The third is replaceable.
Your Machine, Your Rules
When you download Thari Desktop, you get a native macOS application that runs entirely on your hardware. During the initial setup, you connect it to your own Firebase project. The app stores a Firebase service account key — the only credential capable of writing to your Firebase — in your application's sandboxed container. This key never leaves your computer.
Nothing is uploaded until you explicitly choose to share a recording. When you do, the desktop app encodes the video into WebM, chunks it into segments, and uploads them directly to your Firebase Cloud Storage — authenticated by the service account key that only your machine has.
├── config.json # Your Firebase project ID & settings
├── service-account-key.json # Firebase Admin SDK credential
│ # ⚠ NEVER uploaded. NEVER shared.
├── preferences.json # Recording defaults (resolution, fps, etc.)
└── temp/ # Raw recordings before encoding
└── recording-2024-01-15/ # Cleaned up after upload
├── raw-capture.mov
└── encoded/
├── chunk-000.webm
├── chunk-001.webm
└── chunk-002.webmThe service account key is the crown jewel. It grants admin write access to your Firebase project. Because it lives only on your machine, no one else — not even Thari's infrastructure — can modify your recordings, delete your data, or access your storage directly.
Inside Your Firebase Project
When you set up Thari, you create a standard Firebase project (or use an existing one). The desktop app provisions four resources inside it. Here's exactly what each one stores and why.
Cloud Storage
Holds the actual video binary data. Recordings are split into ~5 MB WebM chunks for parallel downloads and resumable uploads.
gs://your-project.appspot.com/
videos/
{video-code}/
chunk-000.webm (5.2 MB)
chunk-001.webm (4.8 MB)
chunk-002.webm (5.1 MB)
...Cloud Firestore
Stores video metadata and timestamped reactions. Structured as a collection of video documents, each with a reactions subcollection.
videos (collection)
└─ {video-code} (document)
├── title: "Q3 Product Demo"
├── description: "..."
├── is_protected: true
├── password_hash: "bcrypt:..."
├── view_count: 142
├── duration_ms: 45200
├── capture_mode: "window"
├── created_at: "2024-01-15T..."
│
└─ reactions (subcollection)
└─ {id}
├── emoji: "🔥"
├── timestamp: 12.5
└── created_at: "..."Cloud Functions
Public GatewayThe only part of your Firebase that is publicly accessible. Acts as a controlled gateway — validates requests, enforces password protection, rate-limits reactions, and serves chunk URLs with temporary signed access.
Cloud Functions read from Firestore and generate signed Storage URLs. They never expose raw credentials or direct database paths.
Security Rules
FirewallFirestore and Cloud Storage are locked down with security rules that deny all direct public access. Every read and write from the outside world goes through Cloud Functions, which enforce your access policies.
rules_version = '2';
service cloud.firestore {
match /databases/{db}/documents {
match /{document=**} {
allow read, write: if false;
// All access via Cloud Functions
}
}
}The Public API Surface
Every Thari instance exposes exactly six HTTPS endpoints. These are the only way anyone — including the official viewer — can interact with your recordings. No backdoors, no admin panels, no hidden routes.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /v/{code} | Fetch video metadata — title, duration, view count, protection status |
| POST | /v/{code}/verify | Verify a password for protected videos. Returns { ok: boolean } |
| POST | /v/{code}/view | Increment the view counter. Fire-and-forget, no response body |
| GET | /v/{code}/manifest | Get the ordered list of chunk URLs for video assembly |
| GET | /v/{code}/reactions | Fetch all timestamped emoji reactions for the video |
| POST | /v/{code}/reactions | Add a new emoji reaction at a specific timestamp |
The /manifest endpoint returns signed Cloud Storage URLs that expire after a short window. The chunk URLs can't be bookmarked or shared separately — they're regenerated on every manifest request.
The Viewer is a Dumb Client
The Thari web player at thari.video is a completely stateless single-page application. It carries zero secrets and has zero special access. It's a static site deployed on a CDN — nothing more.
Zero Firebase credentials
Can't read Firestore or Storage directly
Zero server-side logic
Static HTML/JS/CSS deployed on a CDN
Zero persistent state
No cookies, sessions, or user accounts
Zero write access
Only calls Cloud Functions over HTTPS
What happens when someone opens a link
https://thari.video/v/{project-id}/{video-code}Extract the Firebase project ID and video code from the path.
https://us-central1-{project-id}.cloudfunctions.net/thariThe viewer now knows where to send requests.
GET /v/{code} → { title, is_protected, duration_ms, ... }If protected, show a password form. Otherwise, proceed.
GET /v/{code}/manifest → { chunks: [url1, url2, ...] }Get the list of signed chunk URLs.
Promise.all(chunks.map(fetch)) → Blob → URL.createObjectURLAll chunks download in parallel, concatenate into a single WebM blob in the browser.
<video src={blobUrl} /> → Plyr initializesVideo plays. Reactions load in parallel. View count increments.
The viewer literally does not know your Firebase admin credentials, your Firestore structure, or your Storage bucket name. It only knows the public Cloud Functions URL — the same URL available to anyone with the link.
Deploy Your Own Viewer
Because the viewer is just a static web app with no built-in secrets, you can fork and deploy your own instance. This gives you full control and eliminates any dependency on thari.video.
# 1. Fork the repository
$ git clone https://github.com/niceBoy0419/openloom-viewer.git
$ cd openloom-viewer
# 2. Install & build
$ npm install
$ npm run build
# 3. Deploy to any static host
$ netlify deploy --prod
# or: vercel --prod
# or: npx wrangler pages deploy out/Your own domain
watch.yourcompany.com instead of thari.video
Custom branding
Modify the CSS, swap colors, add your logo
Full UI control
Change the player, add features, remove what you don't need
Zero downtime risk
Even if thari.video goes offline, your recordings are accessible
This is the key insight: your data lives on your Firebase. Your viewer can live anywhere. The two are connected only by public HTTPS endpoints. Swap the viewer, and nothing about your data or infrastructure changes.
Own your recordings
No middleman. No vendor lock-in. Your Firebase, your machine, your rules.