~/thari/technology

How Thari Works
Under the Hood

$ A complete technical breakdown of the end-to-end workflow. From recording on your desktop to playback in any browser — no trust required.

The Architecture

Three independent layers. You own the first two. The third is replaceable.

YOUR MACHINEThari DesktopService Account KeyScreen RecorderVideo EncoderChunk UploaderFirebase Admin SDKcredentials.jsonPRIVATE — never leavesauth writeYOUR FIREBASE PROJECTPRIVATECloud Storagevideos/{code}/chunk-000.webmchunk-001.webmchunk-002.webm...Cloud Firestorevideos/{code}titledescriptionis_protectedpassword_hashview_countduration_mscapture_modereactions/{id}emojitimestampcreated_atSecurity RulesSECURITY BOUNDARYPUBLICCloud FunctionsTHE PUBLIC GATEWAYGET/v/{code}metadataPOST/v/{code}/verifypassword checkPOST/v/{code}/viewincrement viewsGET/v/{code}/manifestchunk URLsGET/v/{code}/reactionsfetch reactionsPOST/v/{code}/reactionsadd reactionHTTPSreactionsTHE VIEWERthari.videoStatic SPANo Firebase credsNo DB accessNo server logicNo write accessNo user sessionsSelf-hostableFork → Build → DeployREPLACEABLE — no lock-inyour infrastructure — you own this
01 — YOUR MACHINE

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.

~/Library/Application Support/Thari/
├── 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.webm

The 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.

02 — YOUR FIREBASE

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 Gateway

The 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

Firewall

Firestore 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
    }
  }
}
03 — PUBLIC API

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.

Base URL Pattern
https://us-central1-{your-project-id}.cloudfunctions.net/thari
MethodEndpointPurpose
GET/v/{code}Fetch video metadata — title, duration, view count, protection status
POST/v/{code}/verifyVerify a password for protected videos. Returns { ok: boolean }
POST/v/{code}/viewIncrement the view counter. Fire-and-forget, no response body
GET/v/{code}/manifestGet the ordered list of chunk URLs for video assembly
GET/v/{code}/reactionsFetch all timestamped emoji reactions for the video
POST/v/{code}/reactionsAdd 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.

04 — THE VIEWER

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

playback sequence
1Parse the URL
https://thari.video/v/{project-id}/{video-code}

Extract the Firebase project ID and video code from the path.

2Construct the API base
https://us-central1-{project-id}.cloudfunctions.net/thari

The viewer now knows where to send requests.

3Fetch metadata
GET /v/{code} → { title, is_protected, duration_ms, ... }

If protected, show a password form. Otherwise, proceed.

4Fetch the manifest
GET /v/{code}/manifest → { chunks: [url1, url2, ...] }

Get the list of signed chunk URLs.

5Download & assemble
Promise.all(chunks.map(fetch)) → Blob → URL.createObjectURL

All chunks download in parallel, concatenate into a single WebM blob in the browser.

6Play
<video src={blobUrl} /> → Plyr initializes

Video 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.

05 — SELF-HOST

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.

deploy your viewer
# 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.