VaultZip is a high-performance DRM (Digital Rights Management) infrastructure designed for secure file storage and licensed distribution. It utilizes Envelope Encryption (AES-256-GCM) to ensure that sensitive digital assets (PDFs, ZIPs, etc.) are never stored in a raw state, providing tamper-proof file retrieval via unique authentication tags.
Key Security Features:
-
On-the-fly Streaming: Files are encrypted as they stream; raw data never touches the server disk.
-
Layered Security: Every file has a unique Data Key, which is itself encrypted by a Master Key. Even a database leak won't expose your files without the Master Key.
-
Cryptographic "Door Guard" & Key Rotation: Features a fail-safe boot-time integrity check that synchronizes environment secrets with an active key versioning system. This architecture enables zero-downtime master key rotation and ensures the application refuses to boot if the environment's
APP_KEYdoes not match the active database version, preventing configuration drift and decryption failures. -
Handshake Architecture: Utilizes a custom CLI for a secure two-step upload process. The backend validates file metadata (like size limits, extensions etc.) during the handshake phase to mitigate "blind upload" attacks, ensuring only compliant streams reach the encryption pipeline.
VaultZip uses a tiered security approach to ensure data is protected at rest and during delivery.
Level 1: Storage Encryption (The Seller's Vault)
When a file is uploaded, it is encrypted using AES-256-GCM with a unique Data Encryption Key (DEK). This DEK is then "wrapped" by the system’s Master Key.
- Status: Raw files never touch the disk. Even if the S3 bucket is compromised, the files are useless without the Master Key.
Level 2: Distribution Encryption (The Buyer's License)
When a user downloads a file, the system performs a cryptographic handshake that ties the data to their specific license. Instead of sending a raw file, the system re-wraps the Data Encryption Key (DEK) using the user's unique License Key.
Process: The file remains encrypted with its original DEK, but that DEK is now protected by the user's License Key rather than the system's Master Key.
Security: This ensures the downloaded .vault bundle is cryptographically locked to the buyer. Even if the file is leaked, it cannot be decrypted without the specific License Key used during the download, preventing unauthorized redistribution.
The final stage of the DRM process occurs entirely on the user's local machine. The server provides a .vault bundle containing the encrypted file and the metadata required to unlock it.
The Decryption Process:
-
Key Derivation: The CLI derives a transient AES key from the user's License Key and the salt provided in the bundle.
-
Key Unwrapping: This derived key is used to decrypt the Wrapped DEK.
-
Stream Decryption: The raw file is decrypted via AES-256-GCM using the recovered DEK and the stored IV.
-
Authentication Check: Before finalizing the file, the CLI verifies the Authentication Tag. If even a single bit of the file was tampered with during transit or storage, the process aborts and the file is deleted.
This project is fully containerized. You only need Docker to get started.
docker compose up --build -dStarts the AdonisJS app, PostgreSQL, and MinIO (S3 storage).
This command creates your user profile and generates a License Key.
-
The "Dual Role" Demo: In this project, you are acting as both the Seller (storing the asset) and the Buyer (using a license to unlock it).
-
Licence Key Storage: The raw license key is saved locally to
./vault_data/.config.json. Do not delete this file; you will need this key later to authorize the "Level 2" decryption and download of your files. -
Security: The database only stores an encrypted version of this license, ensuring the raw key remains in your control.
docker compose exec app node ace vault-zip:register --email=your_emailTo verify that the licence key stored in the database is encrypted:
docker compose exec db psql -U postgres -d vault_zip -c "\x" -c "SELECT email, licence_key FROM users;"Files are encrypted via a streaming pipeline and sent directly to MinIO. Ensure your file is located in the ./uploads_to_process folder.
docker compose exec app node ace vault-zip:upload --email=your_email --title="My very important file" ./uploads_to_process/your_file.pdf🔍 How to Verify the File Encryption
Storage Layer (MinIO)
- Visit the MinIO Console at http://localhost:9001.
- In your
docker-compose.yml, look underservices->app->environment. UseAWS_ACCESS_KEY_IDas the username andAWS_SECRET_ACCESS_KEYas the password. - Locate your file in the vault-zip bucket. Any manual download will result in unreadable binary gibberish, confirming that AES-256-GCM encryption is active and the raw data is protected at rest.
Database Layer (PostgreSQL)
To verify that the cryptographic fingerprints (IV, Auth Tag, and Encrypted Key) are properly stored:
docker compose exec db psql -U postgres -d vault_zip -c "\x" -c "SELECT title, status, file_data FROM file_uploads;"Download your encrypted bundle from the vault. The system re-encrypts the file's access key with your License Key during transit, creating a unique .vault file tied specifically to your credentials.
docker compose exec app node ace vault-zip:download --email=your_emailNote on Security: To prevent your License Key from leaking into shell history or process logs, the command will securely prompt you for the key if it isn't found in your local configuration (./vault_data/.config.json).
Manual Override: If you have a key saved in your config but want to use a different one (e.g., for testing), use the override flag to trigger a fresh secure prompt:
docker compose exec app node ace vault-zip:download --email=your_email --override-keyUpon running the download command, you should see a formatted table of your available files. Select the file you want to download.
🔍 How to Verify the Distribution Encryption
Local Storage Layer (./vault_downloads)
Check your project's directory for a ./vault_downloads directory. You will find a .vault file. Even though this file exists on your hard drive, it remains fully encrypted.
Now that you have the encrypted .vault file, use your licence key to decrypt it. This process is done entirely on the client; your licence key is never sent to the server during this phase.
docker compose exec app node ace vault-zip:decrypt ./vault_downloads/your_file.pdf.vaultNote on Security: To prevent your License Key from leaking into shell history or process logs, the command will securely prompt you for the key if it isn't found in your local configuration (./vault_data/.config.json).
Upon running the command, you should see your now decrypted file in the vault_downloads folder.
Predictable Performance: The decryption process is designed to be memory-efficient even for large files, and maintains a constant memory footprint (approx. 200MB RSS). Because it streams data instead of buffering, decrypting a 108MB file uses about the same amount of RAM as a 108KB file, ensuring stability on low-spec servers or Docker containers.
🔍 How to Verify Authenticated Decryption
Wrong Licence Key
You can attempt to decrypt with a wrong key by manually overriding the licence key in your config using the --override-key flag:
docker compose exec app node ace vault-zip:decrypt ./vault_downloads/your_file.pdf.vault --override-keyYou will be prompted to input a licence key.
File Tampering
You can also manually modify even a single byte of the .vault file (try it in a hex editor).
Note: When testing with a hex editor, modify a byte toward the end of the file. This ensures you are tampering with the encrypted payload rather than the metadata header, allowing you to see the AES-GCM authentication failure in action.
The command will fail and any corrupted output will be automatically deleted to protect your workspace.
Vault-Zip uses a Versioned Encryption System to ensure data remains accessible even as security requirements evolve. Instead of a single static key, the system manages a relationship between database records and environment-level secrets.
-
Key Versioning: Every encrypted file and user licence key is tagged with an
encryption_key_version_id, allowing the system to pick the correct decryption key automatically. -
Zero-Downtime Rotation: Rotate to a new
APP_KEYwithout immediately re-encrypting every file in your storage. -
Environment Auditing: Built-in CLI tools to verify that required keys exist in your
.envbefore attempting operations.
List all registered versions and check if they are active and if their corresponding environment variables are present.
docker compose exec app node ace vault-zip:list-key-versionsSafely transition the system to a new encryption key. This command performs pre-flight checks to ensure the new key is available in the environment before deactivating the old one. In your docker-compose.yml, look under services -> app -> environment.
docker compose exec app node ace vault-zip:rotate-active-key-version --version=2If the provided version already exists in the database, it's is_active status is set to true, and the previous active key is deactivated. Only one key is active at a time for new encryptions.
[!IMPORTANT] Key Syncing Requirement:
After running the command to rotate the key, ensure you update the APP_KEY to mirror the secret of the currently active version in the encryption_key_versions database table.
Why?
The core framework and internal encryption providers use the APP_KEY variable by default for general purpose encryption. To ensure the system-wide encryption remains in sync with your latest rotated version, both variables must point to the same secret.
[!TIP]
To generate a cryptographically secure key, run this command and copy the output from the terminal:
node ace generate:key --showSample Output: HwQ8D8UKcK8c34sdcY3mXkO-pJ9yAnkR
To prevent "silent corruption" (encrypting data with an out-of-sync key), the system implements a strict Boot-Time Hash Validation:
-
Immutable Hashes: When a key version is first activated, a SHA-256 fingerprint of the secret is locked in the database.
-
Fail-Fast Protection: After application boot, the system hashes the current
APP_KEYand compares it against the active database record. -
Atomic Consistency: If the environment secret does not match the database "Source of Truth," the application will refuse to start, preventing accidental encryption with incorrect credentials.
This system prioritizes data integrity over app functionality. Any mismatch in the master key used for encryption causes the app to crash after booting because, in this application, there is no other important feature that can be afforded runtime without resolving master key discrepancies.
The suite provides 100% command coverage, ensuring the system is resilient against malformed data and malicious tampering.
You can run all the tests with one quick command:
docker compose run --rm tester-
Comprehensive Command Coverage: Exhaustive integration tests for
register,upload,list,download, anddecrypt. Every command tested against database and cloud (S3/MinIO) storage drivers. Exhaustive tests are also availabe for thelist-key-versionsandrotate-active-key-versioncommands. -
Strict Validation: Covers all failure modes including missing/duplicate emails, title constraints, file size/type violations, and missing environment variables for the key versioning/rotation tests.
-
Large File Support: Verified handling of multi-megabyte streams near the maximum size limit.
-
Binary & Metadata Integrity: Validates rejection of tampered bundles, malformed JSON metadata, and out-of-bounds headers.
-
Environment Resilience: Tests terminal-width edge cases, license key storage, and interactive prompt overrides.
-
System Cleanliness: Verifies automatic cleanup of all temporary files and "ghost" data after both successful and failed operations.
-
Automated Cleanup: Ensures absolute cleanup of all temporary artifacts and test-generated data (local and S3) after every run.