Last updated: 2026-10-06 ·
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, or into a virtual room (Full VR). 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 machines checked for an update, 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.
Includes an optional voice assistant on the Mac, off by default and only shown if you have a FruitConnect AI plan. When you use it, what you say — and, only if you allow it, a picture of your screen — goes from your Mac to our server and on to the AI provider (section 2.8).
Offers Full VR, a virtual room around your Mac's screen, which runs entirely on the headset: the rooms ship with the app, and nothing about you or your surroundings leaves the headset (section 2.9).
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. Also, only when you activate a FruitConnect AI subscription, sent once to our server together with a one-time proof from Meta so the server can verify the subscription with Meta (section 2.3.1).
Not shared. Never sent to any server we control, except at AI-plan activation (section 2.3.1), where our server verifies it with Meta and keeps only a salted hash.
Until you uninstall or switch Meta account (section 1.4). The salted hash on our server: while your AI subscription is active and 30 days after.
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 and FruitConnect AI subscriptions, read from Meta — on the headset, and, for the AI plans only, by our server through Meta's server API (product, active state, period end, cancellation state) when you activate the plan.
Determines whether your Pro subscription is currently active so the app can unlock unlimited streaming, and whether a FruitConnect AI plan is active so our server can grant and meter it. See sections 2.3 and 2.3.1.
No.
Read at runtime on the headset; for the AI plans, the tier and period end are kept on our server while the subscription is active and 30 days after.
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).
The voice assistant (section 2.8) uses no Meta Horizon Platform feature or data; it runs on your Mac and talks to the model provider described there.
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 is one small counter file per caller and endpoint, named by an HMAC of the mailbox key or of the caller's IP under a server-side secret — never the IP itself, and never a plain hash that could be reversed — and every such file is deleted 24 hours after it was last used. 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 for Pro. Your Meta account ID, billing details, subscription details, and purchase history are not shared with us. For a FruitConnect AI subscription, the activation described in section 2.3.1 sends your Meta user ID with a one-time proof — nothing else.
If you subscribe to a FruitConnect AI plan (Pro + AI 1x or 3x) in the Meta Horizon Store, the headset activates it with our server so that your Mac can use the plan without any account or licence key:
What the headset sends to our server (davidkaiser.dk): your Meta user ID together with a one-time proof issued by Meta for this purpose (a "user proof" nonce), your headset's per-installation identifier (section 1.1) and the app version. Nothing about your Mac.
What our server does with it: asks Meta's server API to confirm the proof and to read whether a FruitConnect AI subscription is active for you — its product, active state, period end and cancellation state (the Subscriptions feature). Billing details, payment methods and purchase history are never read.
What our server keeps: a salted hash of your Meta user ID (not the ID itself), the subscription tier and period end, whether it renews, is cancelled or is a trial, the time of activation, a hash of the activation token it issued, and your headset's per-installation identifier and the app version from the activation. It cannot look you up at Meta again on its own; when the token expires, the headset activates again. Retention: while the subscription is active and 30 days after; then it is deleted automatically, together with the usage counters.
The activation token: stored encrypted on the headset and handed to the paired Mac, which keeps it in the macOS keychain and sends it with each assistant request instead of a licence key. Usage counters (section 2.8) are kept per subscription, never per conversation.
What is never sent: your Anthropic account (there is none in this path), your screen or your voice — those go only as described in section 2.8 when you use the assistant.
2.4 Diagnostic and feedback uploads (opt-in, never automatic)
FruitConnect contains a "Send debug log" button (macOS menu), a "Send Feedback" form (both platforms), and a "Suggest a feature" form on the Quest app's Roadmap page. These three, and only these three, are the 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. Each is described below; none of them runs unless you tap it. 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, clipboard data, file paths from outside the app sandbox, or anything you typed into other applications. It can note where a click or scroll landed, as a position on the screen, and in Full VR where a window was placed (section 2.9).
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 used only to enforce the rate limit, as a small counter file named by an HMAC of the IP under a server-side secret (never the IP itself, never a plain hash), deleted 24 hours after it was last used. (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 8 submissions per IP per 10 minutes is enforced to prevent abuse (a report that also collects the paired device's log counts as two).
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: the rate-limit counter is keyed by an HMAC of the IP under a server-side secret and deleted 24 hours after it was last used).
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.
The Quest app's Roadmap page lets you suggest a feature and later see what happened to your suggestion. This is the only part of the roadmap that stores anything you wrote.
Trigger: Quest → Settings → Roadmap → "Suggest a feature" → type → tap "Send".
What is sent: The suggestion text (≤500 characters) and, if you picked one, a category name (≤40 characters). Nothing else. No account, no Meta user ID, no device identifier, no email, no name, no app version. Those fields do not exist in the request format.
How your suggestion stays yours without identifying you: The server answers with a random one-time token, which is stored only on your headset. The server keeps a SHA-256 hash of that token, never the token itself, so the stored file cannot be used to work out who submitted it — but your app can still prove a suggestion is yours in order to check its status or withdraw it. Each suggestion gets its own token, so two suggestions from the same person are not linkable on the server either.
What the server stores: One small file per suggestion inside signal/roadmap/wishes/, containing the text, the optional category, a status, timestamps, and the token hash. HTTP access to that folder is denied. Your IP address is not stored — it is used only as a rate-limit key, hashed with a server-side secret (HMAC-SHA-256) rather than stored in raw or reversible form, in a counter file deleted 24 hours after it was last used.
Rate limiting: Max 5 suggestions per IP per hour.
Review before anything is public: A suggestion is never published automatically. The Licensor reads it and either declines it or writes a roadmap entry based on it. The published entry is text written by the Licensor — it is not a copy of your submission, and it contains no information about you. Suggestions are not shown to other users at any point.
Retention — two clocks:
A suggestion awaiting review is deleted automatically 90 days after it is submitted. The deletion date is returned to your app when you submit.
When a suggestion is reviewed (accepted or declined), the text and category are deleted immediately at that moment. What remains is a marker with no content — an identifier, the outcome, the dates, and the token hash — kept only so your own app can still tell you what happened. That marker is deleted 30 days after the decision.
Withdrawing it yourself: While a suggestion is awaiting review, the Roadmap page shows it with a Delete button that erases it from the server immediately. No email and no request to the Licensor is required (see also section 2.4.5).
The Roadmap page also lets you vote on suggested features with the ▲/▼ buttons.
What is sent: A feature identifier and a direction ("up" or "down"). Nothing else — no identifier of any kind, and no free text.
What the server stores: Two counters per feature. The server keeps no record of who voted, so there is nothing that could be linked back to you, exported, or erased. As a consequence, "one vote per feature" is remembered only on your own headset; reinstalling the app clears that memory. This is a deliberate trade in favour of storing nothing.
IP handling and rate limiting: Max 40 votes per IP per hour, keyed by the same HMAC of the IP under a server-side secret as described above; the counter file is deleted 24 hours after it was last used. The IP itself is never written to disk.
2.4.5 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 app asks for the standard Android RECORD_AUDIO permission once when you first open it (asking never turns the mic on), and again the first time you turn your mic on if you declined; 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 "What will you use it for?" question, which of the listed options you chose (or that you skipped it). Plus the application version. That is the entire payload.
That question allows several answers. Picking three options sends three event names. Because the server holds only counters, the effect is simply that three totals go up by one; there is no record that they came from the same person, and no way to reconstruct one. A consequence worth stating plainly: the totals add up to more than the number of people who answered.
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 itself written to disk. The rate-limit key is an HMAC of the IP under a server-side secret rather than a plain hash, so it cannot be reversed to an IP even if the rate-limit directory were exposed, and the counter file is deleted 24 hours after it was last used.
Optional: the 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.
Daily-use counter (from Quest version 3.63.706 or later): the first time the app is opened on a given calendar day (UTC), it sends the single event name app_open_daily. Whether it has already been sent today is decided on your headset, from a locally stored "last sent day" date; that date never leaves the device, and the server receives only the bare event name, exactly like the counters above. The server therefore learns "how many headsets opened the app today" as one number — and nothing about which headsets, or whether today's and yesterday's opens came from the same one.
Uninstall counter (from macOS version 3.63.706 or later): when you use the app's "Reset & Uninstall" window and the full uninstall cleanup completes, the application sends the single event name mac_uninstall — a bare +1 with the app version, nothing else — so we can see how many people leave. The farewell screen states this. If you additionally answer the optional "what made you leave?" question, your choice is sent as one event name from a fixed list (the same counter mechanism as above); anything you type in the free-text box goes through the feedback channel described in section 2.4 instead, with its 90-day deletion. Deleting the app by dragging it to the Trash sends nothing — no code runs 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.6.1 Update-check counter (macOS)
The macOS application asks our server whether a newer version exists — once when it starts, and then once an hour while it runs (this is the update check already mentioned in section 2.4). Each request adds one to a plain daily total on the server, so we can tell whether the number of active installations is rising or falling.
What is sent: the application version, and a flag saying whether this is the at-launch check or an hourly repeat. From version 3.63.706 or later the request may also carry a once-per-day flag, sent with the first update check of each calendar day (UTC), so the daily figure counts machines used that day rather than hours of uptime. The decision to send it is made on your Mac from a locally stored "last sent day" date; the date itself is never transmitted. That is the entire addition — the request itself is the same update check the application has always made, and it carries no identifier of any kind.
What is stored: one aggregate figure per day — how many checks arrived, how many of them were launches, how many carried the once-per-day flag, and how many came from each application version. No IP address, no cookie, no device or installation identifier, and no per-request timestamp is written. Two checks from the same Mac are indistinguishable from two checks from two different Macs — now and later, because nothing that could separate them is ever stored.
Retention: the daily totals are deleted after 400 days, kept that long so a year can be compared with the year before.
Legal basis: as with the counters above, no personal data is processed, so the GDPR obligations that attach to personal data do not arise. It is described here for transparency.
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 itself written to disk; 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, and the counter file is deleted 24 hours after it was last used.
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.
2.8 Voice assistant (optional, off by default)
FruitConnect for macOS includes an optional voice assistant you talk to from the headset. It is an AI model, not a person, and it is switched off until you turn it on. Nothing in this section applies unless you use it.
What it is. When you press Talk, your headset microphone is streamed to your Mac over the same encrypted connection as voice chat (section 2.5), transcribed on the Mac, and the text is sent to an AI model whose answer is read back to you. You can also type to it in the assistant window on the Mac. Depending on the permissions you allow, the model may also be shown your Mac screen and may be allowed to act on the Mac.
How the model is reached.The FruitConnect AI plan — the only way for customers. The assistant is part of a FruitConnect AI subscription bought in the Meta Horizon Store; without one, the assistant is not shown at all. No Anthropic account of your own is needed. Your transcript and, only if you allow See the screen, a screenshot are sent from your Mac to our server at davidkaiser.dk and forwarded to Anthropic PBC (USA), which acts as our processor under Anthropic's commercial terms and data processing addendum. Our server forwards the request and returns the answer without keeping the content: it stores only usage counters per subscription (units, turns and token counts per month), never what you said or what was on your screen. How the plan is activated, and what our server learns about your subscription, is described in section 2.3.1.
Bringing your own AI is not offered. Anthropic does not allow third-party products to offer sign-in to a Claude account or to use a customer's subscription on their behalf, and FruitConnect does not let customers use their own Anthropic API key either. The app has no sign-in of any kind, never sees or stores Anthropic credentials, and the option is switched off for every customer licence. It can be enabled by us on our own development licences only; on such a Mac the assistant runs there against that account, and nothing about it reaches other users.
Permissions, and what leaves your Mac at each.Talk (on whenever the assistant is on): your words as text, and the assistant's own notes about your preferences (kept in ~/.fruitconnect/assistant-notes.md and included in each request). See the screen (off by default): additionally a screenshot (JPEG) and the text read from it, only for requests that concern the screen. Control the screen (off by default): the model may click, type, open apps and run Shortcuts, and reads the screen as text to check its work; it cannot read your files or run other commands, and the app blocks any attempt. Run commands on this Mac (off by default, only the paired owner can enable it, separate confirmation): the model may run commands in your user account and read or change files you ask it to — including, if you ask, files such as your Messages database. Whatever it reads at any level becomes part of what is sent to the model provider. Files you add to a message in the Mac's assistant window may be read by the model for that request and sent to the model provider with it. At every level a fixed list of dangerous commands (administrator rights, disk formatting, keychain secrets, disabling security features) is blocked by the app, and the model is instructed to describe and wait for your yes before anything irreversible or outward-facing, and never to handle passwords, keys or card numbers.
Speech recognition. Transcription happens on your Mac: with a local speech model if dictation is installed, otherwise with Apple's Speech Recognition, which runs on-device where macOS supports it for the language and otherwise uses Apple's servers under Apple's privacy policy. The microphone is captured only while Talk is active; with "Keep listening after each answer" (off by default) it stays open between exchanges until you press Stop.
Installing dictation. The local speech model is not part of the app and is never installed without you choosing it — the app offers it during setup, after an update, and when you press a dictation button that cannot work yet. Choosing it downloads two files (about 876 MB in total) from our server at davidkaiser.dk over HTTPS and unpacks them into ~/.fruitconnect/whisper.cpp/; each file's checksum is verified before it is used. The request carries nothing about you beyond what any download does — your IP address and the file requested — and our web server's ordinary access log is the only record of it. No audio, no text and no account information is sent, and the download does not tell us who you are. Once installed, speech never leaves your Mac for transcription at all, which is why it is offered: it replaces Apple's servers for languages macOS does not handle on-device. You can remove it again by deleting that folder, and uninstalling FruitConnect offers to remove it for you. The software and model are third-party open source under the MIT licence, credited in “Open Source Licenses” next to the app.
On your Mac only. Your conversations with the assistant (what you asked and what it answered) are kept in ~/.fruitconnect/conversations/ so you can continue them later. Copies of files you add to a message are kept in ~/.fruitconnect/attachments/. The assistant's notes, its activity log (~/.fruitconnect/assistant-activity.log — every command it ran or was blocked from running, at most 1 MB before rotation), the mailbox files under ~/.fruitconnect/inbox/, and the record of which permissions you allowed, when and from which headset, are stored on your Mac and nowhere else. On a licence where we have enabled our own development option, the API key lives in that Mac's keychain and its status in the app's settings; a customer is never asked for one. None of this is part of "Send debug log" (section 2.4.1). If you ask the assistant to send feedback, it uses the feedback endpoint of section 2.4.2 on your behalf and only after reading the message back to you.
Retention. A conversation stays until you delete it in the assistant window. Deleting it also deletes its attachments, and attachments are removed automatically after 30 days. Other files on your Mac stay until you delete them (section 6). On the FruitConnect AI plan, Anthropic handles your requests as our processor under the terms described above; on a licence where we have enabled the development option, what Anthropic retains under that account is governed by Anthropic's terms, not ours.
Your controls. Nothing beyond talking is on until you allow it: the first time a request needs a permission, the headset asks you before anything is captured or run, and the request is only carried out after you allow it. Allow once covers the one request it was given for — including that request running again right after you allow it — and the next request asks again. Always allow keeps the permission on until you turn it off, and is recorded as your consent. One control — Turn everything off — in the AI menu on the headset and in the FruitConnect menu on the Mac switches every permission off at once, puts them all back to Ask every time, and takes effect for the next action; Permissions lets you review or change them. Stop ends the current work at once. The Assistant activity page on the headset shows the log. The assistant is only shown while a FruitConnect AI plan is active; cancel the plan in the Meta Horizon Store and it disappears again.
2.9 Full VR and headset screenshots
Full VR places your Mac's screen in a virtual room. It runs entirely on the headset: the rooms, skies and sounds ship with the app, and nothing is downloaded when you use them. Your head, controller and hand movements are processed on the headset only, to draw the room and to control your Mac as described in section 2.1, and they are not sent anywhere. The app's own debug log (section 2.4.1) can note a position when you place or recentre a window and which button or pinch was pressed, for troubleshooting — never a continuous trace of where you look, move or point — and that log leaves the headset only if you send a diagnostic report yourself. Passthrough, where you see your real surroundings, is drawn by Meta Horizon OS, and FruitConnect never receives camera images of your surroundings. You can try Full VR without a Mac. In the demo, a placeholder stands where your Mac would appear, and nothing is sent anywhere.
Screenshot (in the Full VR menu) takes one picture of what you see in the headset. Android asks you to confirm screen capture each time, and nothing is captured unless you do. The picture is saved in the headset's Pictures/FruitConnect folder, where you can delete it, and can be copied to your paired Mac's clipboard over the encrypted connection. It is not sent anywhere else.
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, or, if you enable the assistant's See the screen level, to the model provider as described in section 2.8.
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) or are talking to the voice assistant (section 2.8). The microphone is not captured at any other time.
Read or transmit files, emails, or other documents from your Mac, except when you have enabled the assistant's Run commands level and instruct it to (section 2.8).
Ask you for an Anthropic account, an API key or any other AI credential (section 2.8).
Use third-party analytics or crash-reporting SDKs (no Firebase, Crashlytics, Sentry, Bugsnag, Mixpanel, Amplitude, AppsFlyer, or Adjust). The "Send debug log", "Send Feedback" and "Suggest a feature" forms described in section 2.4 are the only mechanisms by which the application can transmit data outside the streaming session, and none of them 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) and the optional voice assistant (section 2.8). The app asks for it once when you first open it — one line explaining why, then Android’s permission dialog — so it can be answered outside Full VR, where the dialog cannot be shown; if you choose Not now or decline, the dialog appears again the first time you turn your mic on or press Talk. Allowing it does not switch the microphone on: the microphone is captured only while your mic is switched on (OFF by default) or while you are talking to the assistant. Deny it and everything except voice chat and the assistant keeps working.
Keeps the connection to your Mac and its audio running while the FruitConnect window is in the background. Android shows a notification while it runs.
FOREGROUND_SERVICE_MEDIA_PROJECTION
The Screenshot button in Full VR (section 2.9): captures one picture of what you see, only after you confirm Android's screen-capture prompt.
POST_NOTIFICATIONS
Shows the notification Android requires while the connection or a screenshot is running.
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) and for Speech Recognition the first time you use the voice assistant without a local speech model (section 2.8). The Accessibility and Screen Recording permissions used for streaming and remote input also cover the assistant's Control the screen level, which additionally triggers the macOS Automation prompt for each app it controls.
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".
Delete assistant data — on the Mac, delete conversations in the assistant window, switch every assistant permission off in the FruitConnect menu, and delete the folder ~/.fruitconnect (conversations, attachments, notes, activity log, mailbox); the consent records are removed by FruitConnect's Uninstall. Our server keeps no conversation content to delete (section 2.8). On a licence where we have enabled the development option, also remove the API key in the assistant window's account menu; data Anthropic holds under that account is deleted through Anthropic.
Delete headset screenshots — delete them in the headset's Pictures/FruitConnect folder (or from Files on the headset).
Delete AI plan data — the activation token on the headset is removed by Settings → "Reset to defaults" or by uninstalling; the Mac forgets it when you "Forget" the headset. Our server deletes the subscription record and usage counters 30 days after the subscription ends; for immediate deletion, email us (section 2.3.1).
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.