ChromVoid Blog

Why ChromVoid Has Its Own Media Player and Notes Editor

Built-in audio, video, and notes are not convenience features bolted onto a vault. They are how ChromVoid keeps common private workflows inside one controlled boundary.

By ChromVoid Team ·

Why ChromVoid Has Its Own Media Player and Notes Editor

Encrypted storage is only one part of private software.

The moment a private file is opened, the product has to answer a harder question:

Where does the decrypted data go next?

For a simple vault, the answer is often "download it" or "open it in another app." That can be the right answer for some workflows. A video editor should use a video editor. A DAW should use professional audio tools. Obsidian on desktop can be a good home for a user's Markdown workflow when the user explicitly mounts a decrypted desktop volume.

But those are different trust boundaries.

ChromVoid added built-in audio playback, video viewing, and Markdown notes because many private workflows should not require exporting decrypted content, creating plaintext mirrors, or depending on another app just to inspect or edit something ordinary.

The goal is not to turn ChromVoid into every app at once.

The goal is narrower:

Common private actions should happen inside the vault boundary by default, and external handoff should be explicit.

The Short Version

ChromVoid keeps files encrypted in the vault and decrypts them only for active work.

For media, that means audio and video should not be fully downloaded into WebView memory before playback. Large media should be served through a short-lived, opaque media source that supports byte ranges. The player asks for the bytes it needs, Core validates the source revision, decrypts the required encrypted chunks, and returns only the requested range.

For notes, that means Markdown should not be treated as a raw text blob or pushed into a mobile filesystem bridge just so another app can edit it. Markdown notes are first-party surfaces inside ChromVoid: rendered preview, source editing, dirty-state protection, stale revision handling, and saves back into the encrypted catalog.

For Android audio, that also means physical playback can use the platform's native media stack while the product state stays in ChromVoid. The system can handle play, pause, seek, headset controls, and notifications through Media3, while filenames and vault paths stay out of system media surfaces by default.

This design gives us tighter lifecycle control and better privacy defaults.

It also costs more engineering effort. Built-in viewers must handle codecs, range reads, stale files, locked vaults, accessibility, mobile layouts, autosave, undo, and failure states. We accept that cost for the workflows that are common enough and sensitive enough to justify it.

Why External Apps Are Not Always The Right Default

External apps are powerful because they are specialized.

They are also outside the vault.

When a vault app hands a decrypted file to another app, several things can happen depending on the platform and workflow:

  1. The file may need to exist as plaintext on disk, even temporarily.
  2. The external app may create caches, thumbnails, recent-file entries, autosaves, indexes, previews, or backups.
  3. The operating system may expose filenames, paths, thumbnails, media metadata, or document activity through surfaces the vault does not control.
  4. On mobile, "open with" and filesystem-provider flows can become platform-specific and hard to explain.
  5. The user may assume data is still protected by ChromVoid even after it has crossed into another app.

That last point matters most.

ChromVoid can warn about a decrypted desktop volume. It can make mounting explicit. It can clean up its own staging files. But it cannot make a third-party app behave like part of the encrypted vault.

So the product boundary is intentionally split.

Desktop users may still choose external workflows. For example, ChromVoid supports a desktop virtual volume model where tools such as Obsidian can work with mounted files. That path is useful, but it is also a decrypted representation for OS applications and must be treated as such.

Mobile notes are different. We rejected the idea of designing a separate mobile mount bridge just to make a phone behave like an Obsidian vault. That would add a large filesystem and sync boundary for a weak default experience. On mobile, the supported notes path is the internal ChromVoid viewer/editor.

Media Is A Streaming Problem, Not A Download Button

The simplest media implementation is full materialization:

  1. Ask Core for the file.
  2. Decrypt the whole thing.
  3. Send all bytes into the WebView.
  4. Create a Blob.
  5. Give the blob: URL to <audio> or <video>.

That works for small files. It is the wrong baseline for large private media.

It delays playback until the whole file is decrypted and transferred. It creates a large plaintext copy in frontend memory. It makes seeking inefficient. It also blurs the difference between "play a range" and "export the file into the UI."

ChromVoid's media architecture uses a different contract for local Core playback.

The WebView still gives the browser media element a normal source URL, but that URL is opaque. It does not contain the original filename, vault path, catalog path, or user-controlled path fragments. Behind that URL, the native layer accepts Range requests, validates the short-lived stream session, asks Core for the requested byte range, and returns 206 Partial Content.

Core does not need to decrypt the entire media file. It calculates which encrypted chunks overlap the requested plaintext range, decrypts those chunks, copies the needed bytes into the response buffer, and stops exactly at the requested range length.

Plaintext memory is intentionally bounded: one decrypted catalog chunk plus the active response buffer.

This is why ChromVoid needed in-app media support. The player is not just a UI convenience. It is the user-facing part of a controlled source lifecycle:

  1. Prepare a source only for a supported local runtime.
  2. Bind it to the current vault session and source revision.
  3. Serve only byte ranges.
  4. Release it on stop, vault lock, app lock, background lock, source invalidation, shutdown, or TTL expiry.
  5. Fall back safely when native streaming is unavailable.

Video uses that shared media-stream idea through the in-app video viewer.

Audio adds a richer product layer because audio has session behavior. Users expect a playlist, current position, next and previous track, mini controls, and the ability to navigate away while playback continues according to the vault lifecycle policy. That state belongs in ChromVoid, not in a hidden DOM element or a platform notification.

Why Android Audio Uses Native Playback

Android adds another layer.

A hidden WebView <audio> element can play sound, but Android system controls do not naturally belong to that hidden element. Quick Settings, lock screen controls, headset buttons, notifications, and OEM media surfaces expect a platform media session.

The old bridge shape is fragile:

System control -> Android service -> WebView -> hidden audio -> WebView snapshot -> Android notification

Every round trip is a chance for UI state, notification state, and real playback state to drift.

ChromVoid's Android-native audio path moves physical playback into Media3/ExoPlayer behind a MediaSessionService. The WebView still owns product-level state: playlist, selected track, full player, mini player, route integration, and user-visible errors. Android owns actual playback execution and system media controls.

That boundary matters.

System play/pause can change ExoPlayer state before WebView reconciliation. WebView then receives native events and updates the ChromVoid UI. The app does not need to rely on notification repaint timing as the source of truth.

Privacy rules remain strict. Android source access uses short-lived opaque vault source tokens. Tokens are scoped to the unlocked vault session, validate source revision, support range reads, and are released on stop, track change, error, vault lock, service destroy, app shutdown, and background lock.

System media metadata is intentionally generic by default. Android surfaces should show ChromVoid audio, not the private filename or display title. The app UI can show the real track name inside ChromVoid, but lock screen and notification surfaces are a different privacy boundary.

Notes Are A Source-Of-Truth Problem

Markdown looks simple until it has to be safe.

A .md file can be private content. It can contain links. It can contain HTML. It can be edited from more than one place. It can be saved with a different byte length. It can be stale because a desktop volume, another client, or another session changed it.

Opening Markdown as a raw <pre> is not enough for a notes product. Sending mobile users to an external editor is not a good default either.

ChromVoid's notes boundary is:

  1. Notes are encrypted catalog files.
  2. The first-party Markdown viewer/editor works on desktop and mobile.
  3. The encrypted catalog remains the persistence source of truth.
  4. The editor does not create a plaintext mirror.
  5. Rendered Markdown is sanitized before display.
  6. User-controlled links do not navigate the WebView by default.
  7. Saves are bound to the opened file identity and source revision.
  8. Stale writes do not silently overwrite remote or external changes.

This turns notes into an app-owned workflow instead of a generic file preview.

The editor can show preview and edit modes. It can keep dirty state in the model. It can intercept close, Escape, browser back, mobile back, route changes, and file switches before unsaved text is destroyed. It can save through the catalog write path and clear dirty state only after the encrypted file update succeeds.

Autosave does not remove the need for correctness. Background save must still use revision protection. It must not silently overwrite stale catalog revisions. Undo and redo should be owned by the model, not by the DOM textarea history, so the user can still recover local edits after an autosave succeeds.

These details are not glamorous. They are what make notes safe enough to be part of a private vault.

What We Gain

The biggest benefit is a clearer privacy boundary.

When audio, video, and notes stay inside ChromVoid, the app can apply the same lifecycle rules as the rest of the vault: lock means stop, release, or invalidate active plaintext access. Source revision changes can invalidate stale streams. Logs can be checked for forbidden fields. UI state can avoid leaking vault paths.

The second benefit is performance.

Range streaming lets playback begin without decrypting a whole large file. Seeking can read the needed byte range instead of rebuilding a full Blob. Frontend memory does not need to carry an entire decrypted media object for normal native playback.

The third benefit is product consistency.

Audio has one playlist model, one mini-player state, one stop/minimize distinction, and one route behavior. Video has an owner for source release and stream errors. Notes have one dirty guard and one stale conflict flow. Components render model state instead of inventing their own local truth.

The fourth benefit is honest platform behavior.

On Android, system media controls belong to Media3. On mobile notes, an internal editor is more realistic than pretending a mounted Obsidian vault is a supported path. On desktop, external tools remain possible through an explicit mounted-volume boundary with a warning that the representation is decrypted for OS apps.

What We Give Up

This approach is not free.

Built-in media and notes increase the amount of code ChromVoid owns. We have to test more lifecycle edges: source release, vault lock, app lock, background lock, stale revisions, unsupported codecs, runtime capability changes, native driver failures, fallback limits, and UI behavior across desktop and mobile.

Built-in viewers will not beat specialized applications at their own jobs. ChromVoid's video viewer is for private playback, not transcoding, editing, subtitle authoring, or codec repair. The audio player is for vault playback, not a full music-library manager. The Markdown editor is for secure notes, not a complete Obsidian replacement with plugin ecosystem, graph view, backlinks, collaborative editing, or CRDT merge UI.

The app also has to be careful not to overpromise.

In-app playback does not mean decrypted bytes never exist. They exist in memory while the user plays media or edits notes. A compromised OS, malicious accessibility layer, screen recorder, or malware running on an unlocked device can still observe user activity. ChromVoid can reduce unnecessary exposure; it cannot make an unlocked hostile device safe.

Fallbacks also remain necessary.

Remote Core dashboards, unsupported runtimes, disabled native media capabilities, or custom-protocol failures may need Blob fallback for smaller files. Oversized files may need an explicit "native streaming unavailable" state rather than silently downloading the whole file. External open/download still matters for workflows that intentionally leave ChromVoid.

Finally, privacy defaults can feel stricter than expected. Showing ChromVoid audio on Android system surfaces is less informative than showing a track title. We chose that default because system media surfaces are outside the app boundary. Exposing filenames there can be added later only as an explicit opt-in product decision, not as an accidental v1 leak.

The Principle

ChromVoid is not trying to replace every tool.

It is trying to make the private path the default path.

For everyday media playback and notes, that means the user should be able to stay inside the vault:

  1. Open an audio file without exporting the whole thing.
  2. Watch a private video without materializing it as a plaintext Blob when native streaming is available.
  3. Edit a Markdown note without creating a mobile plaintext mirror or depending on an unsupported filesystem bridge.
  4. Lock the vault and know active sources are stopped, released, or invalidated.

External workflows still exist, but they should be deliberate.

That is the difference between a vault that stores private files and a vault that also respects what happens when the user opens them.