Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tabuni

Tabuni is a desktop toolkit for macOS, built with Java/JavaFX, aimed at keeping your files organized and your private files actually private. It's a multi-tool app — a left-hand rail switches between independent tools — rather than one do-everything screen, so new privacy-focused tools can be added over time without disturbing the ones that already exist.

Two tools ship today:

  • Files — a fast, drag-and-drop file browser with three one-click cleanup operations layered on top: organize photos/videos/files into Category/Year/Month folders by their real creation date, find and remove duplicate files by checksum, and export a full metadata report (CSV/JSON).
  • Video Vault — encrypt videos into a personal, password-protected .vvlt vault file, then play them back in the app. Nobody without the password — not even someone with full access to the file — can recover the video. Supports encrypting one video at a time or a whole folder at once, and playing one vault or several back-to-back.

Installing

Just want to run Tabuni, not build it? Get a Tabuni-<version>.dmg from whoever built one (see below) and follow INSTALL-DMG.md — drag-and-drop, no Java or Maven needed.

Requirements

  • macOS. The Files module shells out to open -R to reveal files in Finder and uses Desktop.MOVE_TO_TRASH, so it's macOS-only for now.
  • Java 21 (JDK, not just a JRE) and Maven 3.9+. See INSTALL.md if you don't already have these — that guide walks through installing them from scratch, no prior experience assumed.
  • Internet access the first time you build, so Maven can download dependencies (JavaFX, AtlantaFX, Ikonli icons, libsodium/JNA for the vault's crypto, JUnit).
  • exiftool is optional (brew install exiftool) — the Organize/Duplicates tools use it for accurate photo/video creation dates when present, and fall back to filesystem timestamps when it's missing.

Running it

# Development: compiles and launches in one step, fastest way to try changes
mvn javafx:run

# Or build a standalone runnable jar and launch that
mvn clean package
java -jar target/tabuni.jar

# Or package a .dmg to hand to someone else (no Java/Maven needed on their end)
./scripts/build-dmg.sh

mvn clean package also runs the test suite (mvn test on its own runs just the tests). The packaged jar is a "fat jar" (via maven-shade-plugin) — it bundles every dependency, so target/tabuni.jar is the only file you need to hand someone else to run the app, as long as they have Java 21 installed.

Project layout

src/main/java/com/nagacoder/tabuni/
├── Main.java              — JavaFX Application entry point
├── ui/shell/               — Module interface, AppShell (root layout), ModuleRail (the left icon strip)
├── ui/modules/              — one class per tool, registered in ModuleRegistry
├── ui/                      — Files module's screens (MainView, Sidebar, Toolbar, FileTableView, ...)
│                              and Video Vault's screens (VaultLibraryView, VideoPlayerStage)
├── ui/dialogs/              — popup dialogs (Organize, Find Duplicates, Metadata, Encrypt Video, ...)
├── service/                 — the actual logic behind each tool, independent of any UI
├── model/                   — plain data classes passed between service and UI layers
└── vault/                   — the .vvlt encryption format and its crypto primitives

Adding a new tool means writing a class that implements ui/shell/Module.java and registering it in ui/modules/ModuleRegistry.java — the shell, rail, and window-title handling are already generic.

How the encryption works (Video Vault)

The goal: your own computer's disk, and anyone who gets hold of a .vvlt file, never sees plaintext video — only you, with the correct password, can produce it. This isn't hardware DRM: once the app decrypts a frame to play it, that frame exists in normal process memory like any other video player, so it doesn't defend against someone who already has your unlocked computer and the password doing a screen recording. What it defends against is the file itself leaking — a stolen laptop, a backup uploaded somewhere, a shared drive — without the encryption ever having to be undone first.

Where the code lives: com.nagacoder.tabuni.vault (VvltFormat, VvltHeader, CryptoUtils, Packager, Unpacker) implements the format; service/VaultService.java is the only thing outside that package that touches it.

Key hierarchy

Encrypting a video generates a fresh random 256-bit key (the DEK, Data Encryption Key) that does the actual encrypting. Your password never encrypts the video directly — instead:

  1. Your password + a random 16-byte salt go through Argon2id (via libsodium), a memory-hard password-hashing algorithm — 128 MiB of memory and 3 iterations by default — producing a 256-bit KEK (Key Encryption Key). Argon2id is deliberately slow and memory-expensive so that brute- forcing the password (rather than the encryption itself) is impractical, unlike a fast hash like plain SHA-256.
  2. The KEK does exactly one small job: it encrypts (wraps) the DEK with AES-256-GCM.
  3. The DEK encrypts the actual video content, in 1 MiB chunks, also with AES-256-GCM.

Splitting DEK and KEK like this means changing your password later only means re-wrapping a 32-byte key, not re-encrypting the whole video (not currently exposed in the UI, but the format supports it) — and two identical videos encrypted separately still get different DEKs and different ciphertext, even with the same password.

On-disk format (.vvlt)

┌─ HEADER (134 bytes, fixed size) ─────────────────────────────────┐
│ magic "VVLT" (4) │ version (1) │ SHA-256 of plaintext (32)       │
│ Argon2id salt (16) │ mem cost (4) │ time cost (4) │ lanes (1)    │
│ wrapped DEK: nonce+ciphertext+tag (60) │ chunk size (4)          │
│ total plaintext length (8)                                      │
└───────────────────────────────────────────────────────────────────┘
┌─ CHUNK 0 ─┬─ CHUNK 1 ─┬─ ... ─┬─ CHUNK N (partial) ─┐
│ nonce(12)+ciphertext(1 MiB)+tag(16) each, except the last chunk │
└───────────────────────────────────────────────────────────────────┘

Every chunk (bar the last) is exactly the same size on disk, so any chunk's byte offset is header_size + i * (chunk_size + 28) — computable directly, no index needed. That's what would let a future streaming player fetch and decrypt just the bytes around a given playback position instead of the whole file (today's player still fully decrypts up front — see below — but the format was designed with that door open).

Every AES-256-GCM encryption in the format is bound with additional authenticated data (AAD): the wrapped DEK's encryption is bound to the entire rest of the header (so tampering with the KDF params or chunk size is caught as a decrypt failure, not silently misapplied), and each chunk's encryption is bound to its own index (so chunks can't be silently reordered or spliced from a different file). After decrypting, the whole plaintext is also re-hashed and checked against the SHA-256 stored in the header, as a final tamper/corruption check.

Playing a video back

Unlike the format's own streaming-friendly design, Tabuni's player currently takes the simple route: it fully decrypts the vault to a temporary file, then hands that file to a javafx.scene.media.MediaPlayer embedded right in the app (not an external player). The app owns that temp file's whole lifecycle — it's created fresh per playback in its own throwaway temp directory, and deleted the moment you close the player window, rather than lingering on disk.

What each dependency does

Purpose Library
Argon2id (password → key) com.goterl:lazysodium-java (a libsodium binding)
AES-256-GCM (bulk + key-wrap encryption) Java's built-in javax.crypto.Cipher
Embedded video playback org.openjfx:javafx-media

Testing

mvn test

Covers the .vvlt format round-trip (encrypt → decrypt → identical plaintext), wrong-password rejection, exact-chunk-boundary edge cases, and the VaultService glue (temp-file handling, folder listing, name-collision handling).

Known limitations

  • macOS only, for the reasons above.
  • The .vvlt format doesn't store the original file extension, so decrypted temp files are always treated as .mp4.
  • Bulk-playing several vaults at once assumes they all share one password — any that don't are reported as skipped rather than failing the whole batch.
  • No password recovery, by design — losing the password means losing the video. There is nothing to reset.

About

Privacy-focused macOS toolkit built with Java/JavaFX — organizes photos, videos & files by date, finds duplicates, and encrypts videos into password-protected vaults nobody else can open.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages