Security & Privacy

This page explains, in plain language first and technical detail after, how Oscilla handles safety and privacy — what it protects, what it deliberately does not try to do, and how to run it safely. It is written to be read by performers and organisers as much as by developers.

In one paragraph

Oscilla is a tool you run on your own computer, for yourself and for the musicians in the room with you. It keeps no accounts, no passwords, and no personal data — only the scores you make: drawings, audio, images and timing. It is built for a rehearsal room or a venue's local network, not to be published on the open internet. The parts that can change the computer's settings are locked to the machine actually running Oscilla; other people's phones and laptops on the same network can follow and annotate the score, but not reconfigure the software. The code is open source, so anyone can check these claims for themselves.

What Oscilla is, and what that means for safety

Oscilla is a local server. When you start it, your own computer becomes the host, and other devices on the same network — the players' tablets, a second laptop, a projector machine — open the score in a normal web browser by visiting the host's address. There is no Oscilla "cloud": nothing you make is sent to us or to anyone else unless you choose to share the files yourself.

Because everything runs on hardware you control, the security model is the same as any document you keep on your own machine. The important questions are therefore narrow and answerable:

What we protect against

Reaching outside the score folder. Every file Oscilla reads or writes on behalf of a browser is confined to your projects folder. A request cannot use a crafted name (../../ and the like) to climb out and read your private files or write elsewhere on disk. This is enforced in one central place that every file operation passes through, and it is covered by automated tests so it cannot quietly regress. This class of problem was found and closed in a September 2026 security review; the tests exist specifically so it stays closed.

Changing the machine's setup from another device. The powerful operations — changing server or network settings, restarting, updating, installing the desktop launcher, browsing the host's folders from the settings dialog — are host-only. A request for any of them from another device is refused. Editing the score is treated the same way: the host can edit, guests follow.

Untrusted file uploads. When a file is published into a project over the network, its type is checked against an allow-list (scores, audio, images, PDFs, text), its destination is confined to the project, and it is explicitly forbidden from landing in the folder where a project's code extensions live — so an upload cannot smuggle in a script that would later run.

What Oscilla does not try to do

Being honest about the boundaries is part of taking security seriously.

Scores are documents you should trust

A score is more than a picture. It can carry timing logic and — through the ext() feature — small pieces of JavaScript that run in the browser showing the score, to drive custom visuals or behaviour. That power is what makes Oscilla expressive, but it means a score behaves like a document that can contain code, in the same way a spreadsheet can contain macros.

The practical guidance is simple: open scores from people you trust, just as you would only enable macros in a document from a known sender. A score you authored, or one shared by a collaborator, is exactly as safe as they are. The publish-upload path deliberately refuses to place files into the extensions folder over the network, so extensions can only be added by someone with direct access to the host's files — not by a passing network client.

Privacy

Running it safely


Technical detail

For readers who want the specifics behind the summary above.

Trust boundary

Oscilla binds an HTTP (or HTTPS) server and a WebSocket on the host. There is no authentication layer by design: the trust boundary is the network the host is attached to. Two privilege levels exist within that boundary:

requireLocal gates: server log, restart, launcher install/status, update check/apply, server config, OSC config, host directory browse/mkdir, and project migration.

Filesystem containment

All project-scoped filesystem access funnels through a single resolver (resolveProjectDir / resolveProjectFile in serverUtils.js). That resolver:

  1. validates the project name against a strict allow-list pattern (letters, digits, spaces, hyphens, underscores — no dots, no path separators), and
  2. asserts, with a separator-aware prefix check (assertInside), that the resolved path is contained within a projects root — and contains any request-supplied sub-path (audio/…, images/…, versions/…) the same way.

Placing the check at the resolver — rather than at each of ~50 routes — is deliberate: a per-route check is opt-in and was historically forgotten on new routes, which is how a traversal bug recurred. The two directory-listing handlers that do not go through the resolver (/scores/*, /shared/*) strip ., .. and separators from their wildcard before joining. Regression tests live in test/pathSafety.test.js and run under npm test.

Percent-encoded traversal (%2f) is covered: the encoding is decoded only after routing, so the resolver sees real separators and rejects them.

Uploads

Network publish uploads (/api/project/:name/file) validate the project name, normalise and reject ../absolute destination paths, restrict extensions to an allow-list (.svg .json .wav .flac .mp3 .ogg .aiff .aif .png .jpg .jpeg .gif .webp .pdf .txt .md), forbid the extensions/ subtree, and re-assert containment on the resolved destination. Audio/video ingest uploads are size- capped and streamed to disk. JSON request bodies are capped at 10 MB.

Executable content

Projects may contain extensions/*.js, loaded and executed in the browser client via the ext() cue. This is intended capability, not a flaw, but it means a project is executable content and should be trusted like one. The network upload path refuses the extensions/ directory, so extensions can be introduced only by a party with direct filesystem access to the host.

Transport

HTTP by default; HTTPS when a certificate/key pair is supplied. Suitable for a private network. isLocalRequest reads the socket's remote address directly — correct for a direct bind, but note that placing a reverse proxy in front would make every request appear local unless the proxy is configured as trusted and the check moved to a validated forwarded header.

Reporting a problem

Security is treated as ongoing work, not a one-time checkbox: the codebase is open, the containment guarantees are tested, and known follow-ups are tracked in the repository. If you find a security issue, please report it privately to the maintainer (see the repository's contact details) rather than opening a public issue, so it can be fixed before it is widely known.

Tip: use ← → or ↑ ↓ to navigate the docs