Skip to content

// Security

What it sends, and what it does not.

This is the page to forward to whoever signs off on software. Most of what is below is read out of the app release behind the download button and carries the line of source it came from. It runs offline. It is not silent, and this page says what it reaches out for.

What it reaches

The picture is made on your Mac, and nothing about making it needs the network. The app reaches out for four things: to check a licence key, to see whether there is a newer version, to fetch a preview for a link an operator pastes into it, and to report how the app is used — which our privacy page covers in full, including the one switch that turns it off.

The licence request carries 3 fields and no more: license_key, name, license_key_instance_id. The seat is labelled with the Mac's own name so an administrator can tell one machine from another in the vendor's dashboard. There is no API key and no secret in the binary — the endpoints are public ones. It gives up after 10 seconds, and a Mac that has never been licensed makes no licence request at all. Sources/VizallKit/Control/DodoValidator.swift:17 Sources/VizallKit/Control/DodoValidator.swift:62 Sources/VizallKit/Control/LicenseRevalidation.swift:75 Sources/VizallKit/App/AppDelegate.swift:3005

The update check fetches a small XML feed describing the newest signed build, on the updater’s own schedule. The feed and the builds themselves sit on GitHub’s public release pages — the same ones the changelog links to — and a build that is not signed with the key inside the app is refused; how that works is further down this page. Resources/Info.plist:51 Resources/Info.plist:52 Sources/VizallKit/App/AppDelegate.swift:1594

The buy link is not one of these. It is handed to your default browser Sources/VizallKit/Control/DodoValidator.swift:23 and the app never fetches it. Sources/VizallKit/App/AppDelegate.swift:3009

Deploying this somewhere the firewall is explicit? Write to us and we will answer for the build you are about to install, rather than for whichever one this page was last regenerated from.

What it opens on the Mac

Reaching out is one question; listening is the other. The last column is the one that matters. On a fresh install these start on: OSC state out — and everything in that list is addressed to loopback, so it never leaves the Mac.

Endpoint Reachable from What for Read from Ships
UDP 127.0.0.1:9001OSC state out This Mac only Publishes what the visuals are doing, thirty times a second, so a lighting desk or a bridge on the same Mac can follow. It is addressed to loopback, so nothing leaves the machine unless an operator changes where it points. Sources/VizallKit/IO/OSCBroadcaster.swift:9Sources/VizallKit/App/Controls.swift:275 On
UDP 0.0.0.0:9000OSC control in The local network Lets a tablet, a desk or a setlist controller drive the visuals over the LAN. Unauthenticated by design, which is why it ships off and stays off until an operator turns it on. Sources/VizallKit/IO/OSCController.swift:48Sources/VizallKit/App/Controls.swift:284 Off
UDP 0.0.0.0:6454Art-Net TimeCode in The local network Lets a lighting console drive the show playhead with the timecode it already broadcasts. Read-only: the app acts on one opcode and drops everything else on the port, DMX included. Ships off; a Preferences toggle turns it on. Sources/VizallKit/IO/ArtNetController.swift:33Sources/VizallKit/App/Controls.swift:293 Off
TCP 0.0.0.0:9002OSCQuery (HTTP) The local network Serves the app's whole OSC control map as a document a console can read, so a VDMX or Chataigne operator maps by name instead of typing addresses. Advertised over Bonjour. It follows the OSC control switch, so it ships off with it. Sources/VizallKit/IO/OSCQueryServer.swift:29Sources/VizallKit/App/Controls.swift:284 Off
TCP 0.0.0.0:9003OSCQuery (WebSocket) The local network The live half of OSCQuery: streams changing values to a console that asked for them. Same switch as the OSC control port, same LAN consent, ships off. Sources/VizallKit/IO/OSCQueryServer.swift:30Sources/VizallKit/App/Controls.swift:284 Off
TCP 127.0.0.1:9100Local control endpoint This Mac only The JSON endpoint an assistant on the same Mac drives. The socket is bound to loopback rather than filtered per connection, so it is not reachable from another machine at all. Sources/VizallKit/Control/LocalControlServer.swift:65Sources/VizallKit/App/Controls.swift:262 Off

4 of those can be reached from another machine, and they ship off: OSC control in, Art-Net TimeCode in, OSCQuery (HTTP), OSCQuery (WebSocket). It is unauthenticated when it is on — that is what an OSC control port is — so treat it the way you would treat any other desk on the show network.

How that list is kept honest

Every file in the app's source that so much as names a socket API, and what each one is. The sweep is part of the build for this page: a file that starts opening a socket fails it until somebody has written down what the socket does.

  • egress Sources/VizallKit/Control/DodoValidator.swift The licence check. The only code in the app that opens a connection to a host off this machine.
  • none Sources/VizallKit/Control/EgressAllowlist.swift The table of what the app may reach and what for. It is a list of names: it opens nothing, and names a socket API once, in the comment describing the session that enforces it.
  • loopback Sources/VizallKit/Control/LocalControlServer.swift The opt-in control endpoint an assistant drives. Bound to 127.0.0.1, and non-loopback connections are dropped.
  • egress Sources/VizallKit/Control/PinnedSession.swift The one session a fetch of outside input runs on — the preview behind a link an operator pasted. It refuses anything not on the app’s own allow-list before a request is issued, refuses a redirect that would leave it, and cancels a reply bigger than its cap. No cookies, no cache, no credentials.
  • lan Sources/VizallKit/IO/ArtNetController.swift Listens for Art-Net TimeCode from a lighting console so it can move the show playhead. Read-only by construction — it acts on one opcode, drops DMX, and never writes. A LAN listener, and it ships off.
  • loopback Sources/VizallKit/IO/OSCBroadcaster.swift Broadcasts live state as OSC. Its destination defaults to loopback, so nothing leaves the Mac unless an operator points it somewhere.
  • unused Sources/VizallKit/IO/OSCClient.swift An OSC client that can chase a foreign service. Nothing in the shipping app constructs one.
  • lan Sources/VizallKit/IO/OSCController.swift The OSC control port a lighting desk or a tablet drives. A LAN listener, and it ships off.
  • none Sources/VizallKit/IO/OSCDiscovery.swift A pure list model. It names a browser API in one comment and opens nothing.
  • none Sources/VizallKit/IO/OSCPeerPolicy.swift The allow-list that decides which OSC peers may drive the show. It reads the remote address off a connection the OSC control port already holds; it opens nothing of its own.
  • lan Sources/VizallKit/IO/OSCPeering.swift Bonjour browsing for other OSC devices on the network, so a console can be picked from a list instead of typed. It follows the OSC control switch and opens no port of its own — the advertisement lives on the OSC control port.
  • lan Sources/VizallKit/IO/OSCQueryServer.swift The OSCQuery endpoint: a small HTTP server describing every control, a WebSocket streaming live values, and the Bonjour record that lets a VDMX or Chataigne rig find both. Follows the OSC control switch, so it ships off.
  • lan Sources/VizallKit/Remote/PhoneRemoteServer.swift The phone remote: the page a phone on the same network opens to run the show, and the live connection behind it. A LAN listener, and it ships off. A phone that has not paired with the single-use code on screen gets the page and nothing else.
  • none Sources/VizallKit/Studio/StudioSession.swift The one open board on the canvas. It holds the fetcher above rather than being one — a board with nothing on it to preview never builds a session at all.
  • helper Sources/vizall-mcp/HTTPListener.swift The bundled MCP command-line helper, not the app. Loopback unless it is started with an explicit LAN flag.
  • helper Sources/vizall-mcp/main.swift The same helper, talking to the loopback control endpoint above. It reaches no host.

Most of that list opens nothing at all. The ones marked none or unused are there because a sweep cannot tell a mention from a call: one names a discovery API in a comment, the rest are code the shipping app never constructs. The ones marked helper belong to the command-line helper that ships beside the app for an assistant to talk to, not to the app itself.

The app links 39 modules. All but 2 resolve out of Apple's own SDK; the rest are declared dependencies, and the package manifest lists every one of them: github.com/sparkle-project/Sparkle from 2.5.0 Package.swift:9, github.com/PostHog/posthog-ios pinned to 3.69.8 Package.swift:13. Those are PostHog, the analytics SDK that carries the app's usage reporting, and Sparkle, the updater. There is no ad or attribution SDK anywhere in it.

The analytics SDK is the one on that list worth being exact about, and being exact about it is not a sentence — it is what the app reports and the switch that stops it, which is where the whole of it is written down.

What it asks macOS for

5 prompts, each shown once, each with the sentence the app puts in front of the person clicking. Nothing here is asked for silently.

AudioCapture
“Luxaudica listens to your system audio to generate live visuals.” Resources/Info.plist:37
DocumentsFolder
“Luxaudica keeps your Studio boards as ordinary files in Documents ▸ Luxaudica Studio, so you can open, back up and share them.” Resources/Info.plist:39
LocalNetwork
“Luxaudica advertises itself and finds other OSC devices on your local network so consoles and media servers can discover it without you typing an IP address, can follow timecode broadcast by a lighting console, and, when you turn on the phone remote, serves its page to a phone you pair by scanning a code.” Resources/Info.plist:42
Microphone
“Luxaudica uses your audio input to generate live visuals.” Resources/Info.plist:44
SpeechRecognition
“Luxaudica transcribes the audio on your Mac, on-device, to show live captions. Audio never leaves your machine.” Resources/Info.plist:47

There is no screen-recording prompt, because the app draws a picture rather than reading one. The signed bundle requests exactly 1 entitlement — com.apple.security.device.audio-input — and the file contains nothing else.

It is not App-Sandboxed. That is worth stating plainly rather than leaving to be inferred from a badge: the App Sandbox is a requirement of the Mac App Store, and this is a directly distributed app. What replaces it here is the hardened runtime, a Developer ID signature and Apple's notarization — the checks that path actually uses. Scripts/release.sh:32

How the download is signed, and how an update arrives

  • Hardened runtime. The bundle is signed with the runtime hardening option set. Scripts/release.sh:191
  • Developer ID. Signed with a Developer ID Application certificate, with a secure timestamp. Scripts/release.sh:73
  • Notarized. Submitted to Apple and waited on; a rejected build stops the release. Scripts/release.sh:96
  • Stapled. The notarization ticket is attached to the app and to the disk image, so a Mac with no network can still verify it. Scripts/release.sh:233
  • Updates are signed separately. Each release is EdDSA-signed Scripts/release.sh:262 and the public key travels inside the app; a feed or a build that does not match is refused. Resources/Info.plist:53

The update check runs on its own schedule as well as on demand. Resources/Info.plist:55 By default it downloads a new version in the background and installs it when you quit; Settings ▸ Updates turns that off, and then it asks first. Resources/Info.plist:60 If you would rather it never looked, block the update host: the check fails and the app carries on.

What it stores, and where

Location What Read from
~/Library/Application Support/VizallSettings, looks and licence Plain JSON, written and read only by the app. authoring.json · cues.json · factoryRetune.json · factorySeed.json · license.json · logo.json · midi.json · notePresets.json · notes.json · playback.json · presets.json · programBindings.json · sceneBindings.json · segmentVocabulary.json · session.json · source.json · tour.seen · userScenes.json · warpDemotion.json · welcome.seen Sources/VizallKit/App/PresetStore.swift:470
login KeychainLicence anchor One generic-password item holding the licence record — kept in the Keychain because that is where macOS keeps credentials, rather than in a plain file. Sources/VizallKit/Control/TrialAnchor.swift:96
~/Library/Logs/Luxaudica/last-crash.logCrash breadcrumb A backtrace after a hard crash, written for the operator to retrieve. Nothing uploads it. Sources/VizallKit/App/CrashReporter.swift:34
~/Movies/Vizall · ~/Pictures/VizallRecordings and stills Only what an operator asked the app to record or capture. Sources/VizallKit/App/AppDelegate.swift:40

A recording is a file an operator asked for, and it carries the audio that was playing — that is what a recording is. Nothing in that list is transmitted, and the crash breadcrumb is written for somebody to fetch by hand rather than uploaded.

What this website sends

Everything above is about the app. This page reached you over a network too, and a reader who has just been told to check the app against a traffic capture deserves the same list for the site itself: six parties. The citations in this section point into the website’s own source rather than the app’s — and your browser’s network tab is the faster check.

PostHog — the site’s analytics, and it is on. The app carries the same SDK, and what it does with it is privacy’s subject rather than this section’s. The script itself arrives from us-assets.i.posthog.com — a name that appears nowhere in this repo’s source, because the loader builds it at runtime from the ingest host, and the first PostHog line in any network tab. src/layouts/Base.astro:227 Everything after that reports to us.i.posthog.com: which pages get read, what gets clicked, and — when one of these pages breaks in your browser — the JavaScript error it broke with. src/layouts/Base.astro:228 What each of those events carries, and the identifier that makes your second visit count as the same person, is spelled out in privacy.

Cloudflare — the host. Every request for a page here reaches their edge, which is what serving a website is. A second, deliberately small Cloudflare script adds a pageview count and no more — the tally that survives content blockers, kept as a cross-check on the tool above. The script is Cloudflare’s, served from static.cloudflareinsights.com src/layouts/Base.astro:262 — a hostname check in front of it means it runs on luxaudica.com and nowhere else src/layouts/Base.astro:259 — and the count it takes posts back to /cdn-cgi/rum on this site’s own origin, a path Cloudflare’s edge intercepts.

Google Fonts — the type this page is set in. Every page here fetches a stylesheet from fonts.googleapis.com src/layouts/Base.astro:167 and the CSS that comes back pulls the font files from fonts.gstatic.com — a host our own source names only as a preconnect hint, because the request for the faces is written in Google’s stylesheet, not ours. src/layouts/Base.astro:165 Unlike the two scripts above, nothing gates it — no analytics switch, no hostname check; the request rides every load of every page. What Google receives is what any fetched host receives: your IP address, your browser’s User-Agent, and which pages here asked for type.

Dodo Payments — where the purchase happens, and where a licence is managed. This site makes no call to them; it hands your browser to checkout.dodopayments.com when you choose to buy src/config/site.ts:947 and to customer.dodopayments.com, the licence portal, when you recover a key. src/config/site.ts:836 That checkout is the one place in Luxaudica’s licensing life where an email address exists at all: no page on this site asks for one, and the address you type there stays with Dodo as the customer record your licence key is issued against. Links handed over, never fetched by a page here — both appear in your network tab only after you click.

Instagram — the account whose reels sit on the front page. The reels and their covers are copies this site keeps and serves from its own origin src/components/InstagramFeed.astro:18, so loading or playing them asks Instagram for nothing; the follow link, or the one under a playing reel, hands your browser to Instagram only when you click.

YouTube — the demo that plays edge to edge on the front page. Unlike the reels, it is not a copy: it is YouTube’s own player, loaded from www.youtube-nocookie.com src/pages/index.astro:21 with the video’s cover image from YouTube’s image host, and once loaded the player fetches the video and its own code from YouTube’s and Google’s servers. No click asks for it. What YouTube receives is what any fetched host receives, and whatever its player reports about the playback; with reduced motion set, the player still loads but no video is streamed.

What this is not

It is not a certificate.
We hold no audit, no attestation and no compliance mark, and there is nothing here to put a badge on. What this page offers instead is the mechanism, in enough detail to check — which is the thing a badge is a proxy for.
It is not a claim that the app is offline.
It runs offline, which is a different sentence. It activates a licence, it looks for a newer version, it fetches a preview for a link an operator pastes in, and it reports usage until somebody turns that off. Every one of those is named at the top of this page as what it is for, because a page that quietly omitted one would be found by the first person who looked — and the usage reporting, in full, is on privacy.
It is not the privacy policy.
This page is about the app on the Mac, plus one section on the website that serves it — and that section only says where things go. What is collected and kept, and what a purchase involves, is a separate document — read privacy for that, and terms for the licence.
It is not a promise about your network.
The OSC control port is unauthenticated when an operator turns it on, and a show network is a network like any other. Segment it the way you would for a lighting desk.

The questions IT asks

What do we need to allow through the firewall?
Nothing, to use it. Block the network entirely and Luxaudica still runs — it paints, it records, it recalls every look you saved, because the picture is made on the Mac. What a blocked network costs you is licence activation, update notifications, and the usage reporting our privacy page describes. A licensed Mac that cannot reach the licence vendor keeps its licence; an unreachable vendor is treated as a network problem, not as an answer. If you need the exact hosts to allow for a deployment, write to us and we will answer for the build you are about to install.
Does it need an account, a sign-in or a directory integration?
No. There is no account to create and nothing to connect to a directory. A licence is a key an operator pastes in once; until that happens the app makes no licence request at all.
Is the audio sent anywhere?
No. Audio is analysed on the Mac, in memory, frame by frame, and is never transmitted — not the sound, and not the analysis of it. That holds whatever else the app is doing on the network. Captions are transcribed on the Mac as well; the permission prompt macOS shows says so in the app's own words: “Luxaudica transcribes the audio on your Mac, on-device, to show live captions. Audio never leaves your machine.”
Is it in the Mac App Store, and is it sandboxed?
Neither. It is distributed directly, signed with a Developer ID certificate, notarized by Apple and stapled, which is the direct-distribution path. The App Sandbox belongs to the other path, and the app does not request it — the entitlements file asks for one thing, com.apple.security.device.audio-input, and stops.
How do updates arrive, and can we stop them?
The app checks a signed feed on its own schedule; a build whose signature does not match the public key inside the app is refused. By default it downloads a new version in the background and installs it when you quit; Settings ▸ Updates turns that off, and then it asks first. Block the update host and the check simply fails — nothing else changes.
What does it leave on the machine?
~/Library/Application Support/Vizall, login Keychain, ~/Library/Logs/Luxaudica/last-crash.log, ~/Movies/Vizall. Everything there is written by the app for the app; our privacy page covers what the website does, which is a separate question.
Can we verify any of this ourselves?
Yes, and we would rather you did than take our word for it. The download is signed and notarized, so codesign and spctl will tell you who built it before you run it once, and a traffic capture will tell you what it reaches. Most of what is below carries the file and line number in the app source it was read from, at the release behind the download button, generated from that release rather than typed. If you want us to answer for a specific build before it goes on your machines, ask.

Read next: flash safety · what a licence covers · privacy · the assistant and the local endpoint