Last updated: 2026-08-03 ·
Effective date: 2026-08-03 ·
Contact: David Kaiser, fruitconnect@davidkaiser.dk Applications: FruitConnect for macOS (companion application) and FruitConnect for Meta Quest (Quest 2 / Pro / 3 / 3S)
Plain-language summary. FruitConnect streams your Mac's screen to a 2D panel inside Meta Quest. We build no profile of you and track no individual: there is no advertising, no third-party analytics, no behavioural data, and nothing is sold, shared, or rented. The only things we count are anonymous totals — how many people opened the app, how many pages were viewed, what was typed into the FAQ search — kept as bare numbers with no identifier attached (sections 2.6 and 2.7). Your screen content, keystrokes, and files never reach any server we control.
What the app actually does:
Stores a per-installation device identifier and your paired Macs locally on the headset.
Relays encrypted pairing handshakes through a public mailbox endpoint when you pair over the internet (Remote Access).
Uses public STUN servers to negotiate a peer-to-peer WebRTC connection between Quest and Mac.
Captures your microphone only while you have the optional voice chat switched on (off by default), and sends it peer-to-peer, encrypted, to the connected device — never to any server (section 2.5).
Communicates with Meta's In-App Purchase service if you upgrade to Pro.
Uploads a debug log only when you explicitly tap "Send debug log" in the macOS menu — never automatically and never on crash.
Meta Horizon Platform data we access (Data Use Checkup)
FruitConnect uses the following Meta Horizon Platform features, requested through Meta's Data Use Checkup. This section articulates, in Meta's own terms, every category of Developer User Data and User Data the app collects, uses, and processes; the numbered sections referenced below give the full technical detail.
Platform feature
Data we access
How we use it
Shared with third parties?
Retention
User ID
Your Meta user identifier.
Binds each paired Mac to your Meta account so a different account signed in on the same headset cannot inherit or access your saved pairings (anti-hijack security). See section 1.4.
No — never sent to any server we control.
Until you uninstall or switch Meta account (section 1.4).
User profile
Your Meta Horizon username (display name). This permission also exposes your profile photo, which FruitConnect does not access or use.
Shows your display name on your paired Mac(s) so you can see who is connecting; you can override it in Settings. See section 1.4.
No.
Same as User ID (section 1.4).
In-app purchases
A purchase request for the Pro SKU and a Meta-validated entitlement/receipt (Pro: yes/no).
Unlocks the optional Pro upgrade. See section 2.3.
No — billing details and purchase history are never sent to our servers.
Not stored by us.
Subscriptions
Your subscription status and billing-period start/end time for the Pro subscription, read from Meta.
Determines whether your Pro subscription is currently active so the app can unlock unlimited streaming. See section 2.3.
No.
Read at runtime; not stored on our servers.
FruitConnect does not use Meta's User age group data. The app serves a single audience rated 13+ (Teens & Adults) and applies no age-based logic in the app (see section 5, Children's privacy).
1. Data stored on your device
All data below is stored exclusively on your Quest headset. None of it leaves the device unless you take an action that requires it.
1.1 Device identifier
What: A randomly generated UUID created on first launch.
Where: Encrypted preferences on the headset.
Why: Identifies this Quest installation to a Mac it has paired with, so the Mac knows which device is reconnecting.
Retention: Until you uninstall the app. The app declares android:allowBackup="false", which means uninstalling permanently deletes this identifier.
1.2 Paired Macs
What: For each Mac you have paired: pairing ID, device name (your Mac's mDNS hostname), public key material, last-known transport (USB / Wi-Fi / internet), and capability flags.
Where:EncryptedSharedPreferences using AES-256-GCM for values and AES-256-SIV for keys, with a master key in the Android Keystore (hardware-backed where the headset supports it).
Why: Lets the app reconnect to a previously paired Mac without re-entering a code, and prove pairing identity using a zero-knowledge protocol.
Retention: Until you tap "Forget" on a Mac, "Forget All" in Settings, or uninstall the app.
Why: Restore your preferred settings between sessions.
Retention: Until you reset to defaults or uninstall.
1.4 Meta account identifier (v3.35+)
What: Your Meta user identifier (the User ID feature) and Meta display name (the User profile feature) as returned by the Meta Horizon Platform SDK on Quest.
Profile photo: The Meta User profile permission that returns your display name also grants access to your profile photo. FruitConnect reads and uses the display name only — it does not access, store, transmit, or display your profile photo.
Where: On Quest, in app-private storage. On Mac, alongside each paired-Quest record in macOS UserDefaults.
Why: Two reasons. (1) Security: each paired Mac record is bound to a specific Meta user so a different account logging into the same Quest hardware cannot inherit a previous user's pairings. On Quest, switching Meta accounts wipes the local paired-Macs list at next app launch. On Mac, every connect is gated on the incoming Quest's Meta user matching the stored value — mismatch automatically removes the pair. (2) UI: the Mac shows the Meta display name in its "Paired Quests" list so the Mac user can see who is connected, not just what device.
Wire-path: peer-to-peer ONLY. The Meta identifier never reaches any server controlled by the Licensor. It travels:
Over the encrypted LAN socket (ChaCha20-Poly1305-AEAD) directly between Quest and Mac, OR
Over the encrypted WebRTC DataChannel inside the rendezvous mailbox payload — which is itself ChaCha20-Poly1305-AEAD encrypted client-side BEFORE upload, so the rendezvous server only ever sees opaque ciphertext (zero-knowledge — see section 2.2.1).
Debug log redaction. The Meta identifier is explicitly excluded from every debug-log entry on both Quest and Mac. Diagnostic uploads (which only happen when you tap "Send debug log" — see section 2.4) therefore cannot leak the identifier even via the debug-log payload. Log entries say metaUser=(present) / metaUser=(absent) and (ids redacted from log) instead of the raw value.
Retention: On Quest, until you uninstall or switch Meta account (whichever comes first). On Mac, until you "Forget" the pair from the menubar, factory-reset the org, or uninstall the Mac app.
Permissions: This feature uses the OVR Platform SDK's logged-in-user API. On Meta Quest Store builds, the SDK returns the user info silently (entitlement-based access). On sideloaded / dev-mode builds without entitlement, the SDK returns no user info — the app continues to work with the legacy v3.34 device-only behavior (Mac shows "Quest 3" in the UI as before; no per-user security gate is applied to that pair).
2. Data sent over the network
2.1 Local network (LAN) pairing and streaming
When your Mac is on the same Wi-Fi or USB-tethered, all communication stays inside your local network.
Pairing: Bonjour / mDNS discovery; the pairing handshake (6-digit code or QR code) exchanges public keys directly between Quest and Mac.
Streaming: Encoded video and audio frames sent from Mac to Quest over a direct TCP socket, encrypted with ChaCha20-Poly1305 using a key derived from your pairing token (HKDF-SHA-256). The key never leaves your devices, so no one on your local network can read the stream.
Input: Mouse and keyboard events sent from Quest to Mac over a separate TCP socket, also ChaCha20-Poly1305-encrypted.
No server in this path. No traffic leaves your local network.
2.2 Internet pairing and streaming (Remote Access)
If your Mac is not on the same network, FruitConnect can connect over the internet.
Purpose: A zero-knowledge message-relay that lets Quest and Mac exchange short signaling payloads while neither side knows the other's IP yet.
What is sent: ChaCha20-Poly1305-encrypted signaling envelopes (WebRTC SDP offers/answers, ICE candidates), addressed by opaque mailbox keys derived locally on each device from your shared pairing token via HMAC-SHA-256. The pairing token itself never leaves your devices.
What the server stores: Up to 64 short encrypted messages per opaque mailbox key, automatically deleted after 120 seconds (whichever comes first). On disk, only a SHA-256 hash of the mailbox key is used as the filename — the raw key is never written to disk.
What the server cannot do: Cannot read the conversation, cannot impersonate either side, cannot link mailbox keys back to a specific user or device because keys rotate every 5 minutes.
Rate-limiting: Scoped per opaque mailbox key, not per IP — no IP address is hashed into rate-limit state.
Application-level logs: The application code does not write IP addresses or user identifiers to disk; rate-limiting state stores only SHA-256 hashes of opaque keys. Standard webserver access logs (containing IP, timestamp, and request path) are maintained automatically by the hosting provider's default rotation policy and used solely for abuse prevention; FruitConnect does not access, analyse, or export them.
2.2.2 STUN servers
Purpose: Help WebRTC determine each peer's public IP and negotiate a direct connection.
STUN endpoints used:
stun:stun.l.google.com:19302 (Google)
stun:stun1.l.google.com:19302 (Google)
stun:stun.cloudflare.com:3478 (Cloudflare)
stun:stun.1und1.de:3478 (1&1 IONOS)
stun:stun.t-online.de:3478 (Deutsche Telekom)
What STUN servers see: Your public IP and the source port of the STUN request. They cannot see media content. No third-party analytics are loaded from these endpoints.
2.2.3 TURN relay (optional, build-time)
If a TURN relay is enabled in this build (some networks behind symmetric NAT require it), encrypted media bytes may be relayed through the configured TURN provider. The TURN provider sees encrypted bytes only — they cannot decrypt video, audio, or input events.
2.2.4 Encrypted media stream
Once Quest and Mac have established a WebRTC connection (via the rendezvous mailbox and STUN), all video frames, input events, and control messages are end-to-end encrypted using DTLS-SRTP (the WebRTC standard). Neither we nor any third party can read the contents of this stream.
2.3 Meta Horizon Platform (In-App Purchases & Subscriptions)
If you tap the Pro upgrade button, or whenever the app checks whether your Pro subscription is active:
Endpoint: Meta's official In-App Purchase and Subscriptions service via the Meta Horizon Platform SDK.
What is sent (In-app purchases feature): A purchase request for the Pro SKU and your Meta Horizon entitlement.
What we receive (In-app purchases feature): A boolean entitlement (Pro yes/no) and a purchase receipt validated by Meta.
Subscriptions data (Subscriptions feature): Because Pro is a recurring subscription, the app reads your subscription status and billing-period start/end time from Meta to determine whether your Pro subscription is currently active and therefore unlock unlimited streaming. This is read at runtime on the device; it is not written to, aggregated by, or shared from any server we control.
What we send to our servers: Nothing. Your Meta account ID, billing details, subscription details, and purchase history are not shared with us.
2.4 Diagnostic and feedback uploads (opt-in, never automatic)
FruitConnect contains a "Send debug log" button (macOS menu) and a "Send Feedback" form (both platforms). These are the only ways the application transmits anything you wrote or recorded to a server controlled by the Licensor, outside of the rendezvous mailbox described in section 2.2. The application also performs licence validation (section 2.3), checks for updates, and increments the anonymous counters described in section 2.6 — none of which carry content you authored.
Trigger: A button tap by you. The application does not have a crash handler, an uncaught-exception handler, a scheduled background uploader, or any analytics/crash-reporting SDK that could collect or transmit data without an explicit tap. The presence or absence of crashes has no effect on what is sent.
Cross-device button: When sending a diagnostic from one device, you may opt in to also collecting the log from the other paired device. The other device receives the request over the established WebRTC DataChannel and uploads only after you confirmed the action that triggered the request — there is no out-of-band channel that lets the Licensor request a log from your device.
Trigger: Mac menu → "Send debug log" → fill in form → tap "Send".
What is sent:
Free-text fields you typed: name (≤100 chars), email (≤200 chars), message (≤8 000 chars), occurrence time/frequency.
Lifecycle counters: total app launches, number of unclean exits in the last 24 hours, a short description of how the previous session ended.
System info: macOS version, Mac model, locale, app version, screen resolution, and similar non-personal environment metadata (capped at 40 keys / 200 chars per value).
The contents of the rolling debug log file (plain text, capped at 5 MB on the client and 8 MB on the server). The log records what FruitConnect itself wrote — connection events, error messages, performance counters. It does not record screen content, keystrokes, mouse coordinates, clipboard data, file paths from outside the app sandbox, or anything you typed into other applications.
What the server stores: The metadata fields and the log file are written to a folder inside signal/diagnostic_inbox/, which is not exposed on the public web (HTTP access is denied by .htaccess); only the Licensor can retrieve submissions over an authenticated FTP channel. The application does not store your IP address with the submission — your IP is only used in-memory by the server to enforce rate limits, where it is hashed (SHA-256) and never written to disk in raw form. (The hosting provider keeps standard webserver access logs containing IP, timestamp, and request path on its own short rotation, used solely for abuse prevention; see section 2.2.1.)
Rate limiting: A maximum of 3 submissions per IP per 10 minutes is enforced to prevent abuse.
Retention: Each submission is automatically deleted 90 days after it is received. The exact deletion date is recorded in the metadata file alongside the submission as an expiresAt timestamp, and is also returned to the application in the upload response. Cleanup runs opportunistically on each new submission, so no separate scheduler is required. The Licensor does not aggregate, profile, or share these reports.
Trigger: "Send Feedback" form (Mac menu or Quest Settings) → fill in → tap "Send".
What is sent: Optional name (≤100 chars), optional email (≤200 chars), required message (≤8 000 chars), and a short string identifying which device sent it ("mac" or "quest"). No log file is attached.
What the server stores: A plain-text file with the four fields, inside signal/feedback_inbox/ (HTTP access denied; FTP-only retrieval). Your IP address is not stored with the submission (same handling as for diagnostic submissions: in-memory rate-limit hashing only).
Rate limiting: Max 5 submissions per IP per minute.
Retention: Each submission is automatically deleted 90 days after it is received. The deletion date is written into the file header as an Expires: line. Cleanup runs opportunistically on each new submission.
2.4.3 Your control
The buttons are clearly labelled. You can use FruitConnect indefinitely without ever tapping them, and nothing diagnostic-related leaves your devices.
Email and name fields in both forms are optional. If you leave them blank, the resulting submission contains only the technical content (log + system info for diagnostic; message text for feedback) — no information that ties it back to you personally.
All submissions auto-delete after 90 days regardless. To request earlier deletion of a previously sent submission (for example, under the GDPR right to erasure), email fruitconnect@davidkaiser.dk with the approximate time and a snippet of the message and it will be removed within 30 days.
2.5 Voice chat (optional, strictly opt-in)
FruitConnect includes an optional voice-chat feature (protocol v5) so the people connected to the same Mac — for example the owner and an invited guest in a co-op session — can talk to each other.
Opt-in on every device, OFF by default. The microphone is captured only after you turn your own mic on with the mic button. On Quest, the standard Android RECORD_AUDIO permission dialog appears the first time you do this; on macOS, the system microphone permission dialog appears. If you deny the permission, the rest of the app keeps working — voice simply stays unavailable.
What is captured and sent: While your mic is on, microphone audio is encoded on-device (AAC mono, 32 kbps) and transmitted as a real-time stream to the device(s) in the same session.
Transport — the same encrypted peer-to-peer paths as everything else: over LAN/USB, voice frames travel on the ChaCha20-Poly1305-encrypted direct socket (section 2.1); over Remote Access, they travel on the end-to-end encrypted WebRTC connection (section 2.2.4). Voice audio never reaches any server we control — the rendezvous mailbox relays encrypted signaling only, never audio.
No recording, no storage: Voice frames are decoded, played, and discarded in memory. Neither device writes voice audio to disk, and voice content never appears in debug logs or diagnostic uploads (section 2.4) — logs record connection events, not audio.
Host control: The person at the Mac (host) can mute any connected guest's microphone at any time; a muted guest sees "Mic muted by host" and cannot re-enable their mic until the host unmutes them.
Meeting-mode routing (Mac, local only): The Mac can optionally route incoming guest voice into a virtual "FruitConnect Microphone" loopback device, so a meeting app you run (e.g. Zoom or Teams) can pick up the guest as its microphone input. This routing happens entirely inside your Mac and creates no additional network path.
2.6 Anonymous onboarding counters
The Quest application increments a small number of anonymous counters during first-run setup, so we can tell how many people open the app and how many of them turn out not to own a Mac (without one, FruitConnect has nothing to connect to). Nothing here identifies you, and nothing is stored about you as a person.
What is sent: one event name from a fixed list — that the welcome screen was shown, which of the two buttons on it you tapped, and, if you answer the optional "How did you find FruitConnect?" question, which of the listed options you chose (or that you skipped it). Plus the application version. That is the entire payload.
What is NOT sent: no device identifier, no licence key, no account, no name, no e-mail, no free text of any kind. The question offers fixed choices only — there is no text field, so it is not possible to submit personal information through it even by accident.
How it is stored: the server keeps counters, not records. Each event increments an integer in a single aggregate file (a lifetime total, a per-day total and a per-version total). No row exists for any individual submission, so there is nothing that could be linked back to a person, exported, or erased on request — there is no personal data to act on.
IP addresses: as with every other endpoint (section 2.4), the caller's IP is used only to enforce a rate limit and is never written to disk. For this endpoint the rate-limit key is an HMAC of the IP under a server-side secret rather than a plain hash, so the key cannot be reversed to an IP even if the rate-limit directory were exposed.
Optional: the source question can be skipped, and skipping is recorded only as "skipped".
The optional "What are you hoping to use it for?" box is NOT part of these counters. Free text cannot be counted, and cannot be assumed harmless — so it is submitted as ordinary feedback under section 2.4 instead, with the 90-day retention described there. No name or e-mail is attached to it, and leaving it blank sends nothing at all.
Legal basis: because no personal data is processed, the GDPR obligations that attach to personal data (consent, access, erasure, portability) do not arise for this feature. It is described here for transparency, not because it collects anything about you.
2.7 The website (davidkaiser.dk/fruitconnect)
The website sets no cookies at all — not for analytics, not for preferences, not for sessions — and stores nothing in your browser. There is no consent banner because there is nothing to consent to. Two things are counted on our own server, both as plain totals:
Page visits. Each page increments a counter for that page and that day. What is stored is a number per page per day — nothing else. No IP address, no browser fingerprint, no referrer, no identifier of any kind is written, so individual visits cannot be separated from one another, let alone traced to a person.
FAQ searches. If you type into the search box on the FAQ page, the phrase you searched for is recorded together with a count of how often it has been searched. This exists for one reason: to show us which questions people ask that the page fails to answer, so we can write those answers. The phrase is stored on its own — not with your IP address, not with a cookie, not with a session or visit identifier, and not in any order that could reconstruct one person's searches. Identical phrases are merged into a single count. Anything resembling an e-mail address, a telephone number, or a licence key is discarded before it can be written, and entries are deleted 90 days after they were last searched. The search box itself works entirely in your browser; nothing is sent until you stop typing, and only then the finished phrase.
As with every endpoint (section 2.4), the caller's IP address is used only to enforce a rate limit and is never written to disk; for the search endpoint the rate-limit key is an HMAC of the IP under a server-side secret, so it cannot be reversed to an IP even if that directory were exposed.
We would rather be exact than flattering: a search phrase is free text, and free text is the one thing a person could accidentally type something personal into. That is why it is filtered, capped in length, stripped of everything around it, and deleted after 90 days — and why it is described here in full rather than folded into a claim that we collect nothing.
3. Data we do not collect
To make this concrete, FruitConnect explicitly does not:
Track your usage, sessions, or session duration on a server we control.
Record, store, or transmit the contents of your Mac screen except as encrypted real-time frames in the active streaming connection.
Record, store, or transmit your keystrokes, mouse movements, or clipboard content except as encrypted real-time input events in the active streaming connection.
Record, store, or transmit your voice except as an encrypted real-time audio stream while you have voice chat switched on (section 2.5). The microphone is not captured at any other time.
Read or transmit files, emails, or other documents from your Mac.
Use third-party analytics or crash-reporting SDKs (no Firebase, Crashlytics, Sentry, Bugsnag, Mixpanel, Amplitude, AppsFlyer, or Adjust). The "Send debug log" and "Send Feedback" buttons described in section 2.4 are the only mechanisms by which the application can transmit data outside the streaming session, and neither is invoked unless you explicitly tap it.
Show ads or use ad networks.
Sell, rent, license, or share any data with advertisers, data brokers, or third parties for marketing.
4. Permissions the app requests
Android permission
Why we request it
INTERNET
Required for Remote Access (internet pairing/streaming). Not used during LAN-only sessions.
ACCESS_NETWORK_STATE
Detect whether you are online before attempting Remote Access.
ACCESS_WIFI_STATE
Detect Wi-Fi changes to auto-upgrade from Remote Access to LAN when your Mac becomes reachable.
CAMERA (runtime prompt)
Scan the QR code shown on your Mac when pairing. The camera feed is processed locally on the headset by ML Kit's barcode scanner; no frames leave the device. Granted only when you open the QR-scan screen, and you can deny it and use the 6-digit code instead.
horizonos.permission.HEADSET_CAMERA
Quest-specific equivalent of the Android camera permission, required by the QR scanner.
com.oculus.permission.HAND_TRACKING
Allows the UI to respond to hand-tracking input as an alternative to controllers.
MODIFY_AUDIO_SETTINGS
Audio focus and volume control for Mac audio and voice playback on the headset. Playback control only — this permission cannot access the microphone.
RECORD_AUDIO (runtime prompt)
Optional voice chat (section 2.5). The permission dialog appears the first time you turn your mic on; the microphone is captured only while your mic is switched on (OFF by default). Deny it and everything except voice chat keeps working.
The app does not request location, contacts, calendar, storage, SMS, telephony, or any other permission. On macOS, the companion app additionally asks for the system microphone permission the first time you enable voice chat there (see section 2.5).
5. Children's privacy
FruitConnect is not directed at children under 13. The Meta Horizon Store age rating for this app reflects this. We do not knowingly collect personal data from children. If you believe a child has used the app on your headset, uninstalling the app will permanently delete all locally stored data.
6. Your rights
You can at any time:
Delete a paired Mac — Settings → My Macs → "…" → Forget.
Delete all paired Macs — Settings → My Macs → "Forget All".
Reset all preferences — Settings → "Reset to defaults".
Permanently erase all locally stored data — Uninstall the app from Quest Home. Because allowBackup is disabled, no backup of FruitConnect data exists in your Meta account or any cloud.
For privacy questions or to exercise other rights, email fruitconnect@davidkaiser.dk and a response will be sent within 30 days.
If you are in the EU/EEA or UK, you have rights under GDPR including access, rectification, erasure, restriction, portability, and objection. Use the same email above to exercise these rights.
7. Data security
Paired Mac records are encrypted at rest using AES-256-GCM (values) and AES-256-SIV (keys), with a master key in the Android Keystore (hardware-backed where supported).
Pairing handshakes use X25519 (Curve25519) ECDH and HKDF-SHA-256 to derive session keys, with rendezvous-key addressing rotated every 5 minutes to prevent correlation attacks.
Signaling payloads exchanged through the rendezvous mailbox are encrypted client-side with ChaCha20-Poly1305 AEAD before upload.
Streaming media is end-to-end encrypted via DTLS-SRTP (the WebRTC standard).
The rendezvous mailbox enforces a 32 KB payload cap, a 64-message limit per opaque key, automatic deletion after 120 seconds, and per-key rate-limiting.
8. Open source attribution
FruitConnect is built on top of several open-source libraries (most notably WebRTC and its sub-components, plus Jetpack Compose, Kotlin, AndroidX, CameraX, and Google ML Kit on the Quest side). The complete list of components and their licenses is shipped with the application:
macOS: open the FruitConnect menu in the menu bar and click "Licenses" in the footer.
Quest: open Settings inside FruitConnect on the headset and tap "Open Source Licenses".
We may update this policy as the app evolves. The "Last updated" date at the top changes when we do. Material changes that affect what data is collected or how it is used will be highlighted in-app the next time you launch FruitConnect after the update is published.