npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@h3l1os/mp4vault

v2.1.0

Published

Hide and extract files within MP4, JPEG, and PNG containers with AES-256-GCM encryption

Downloads

44

Readme

@h3l1os/mp4vault

Hide and extract files within MP4 video containers with optional AES-256-GCM encryption.

Features

  • Embed any file (text, images, binaries) inside an MP4 container
  • AES-256-GCM authenticated encryption with key or password
  • Mix public and encrypted files in the same container
  • Attach metadata to embedded files
  • Preserves MP4 playability — output files remain valid MP4 videos
  • Dual ESM/CJS output
  • Node.js 18+

Install

npm install @h3l1os/mp4vault

Usage

Embed a file with key encryption

import { MP4, Convert, Writable } from '@h3l1os/mp4vault';

const key = Convert.hexStringToBuffer('000102030405060708090a0b0c0d0e0f');

const mp4 = new MP4();
mp4.setKey(key);
await mp4.loadFile({ filename: 'video.mp4' });

await mp4.embedFile({ filename: 'secret.pdf' });

const writable = new Writable({ filename: 'output.mp4' });
await mp4.embed(writable);

Embed a file with password encryption

const mp4 = new MP4();
mp4.setPassword('my-secret-password');
await mp4.loadFile({ filename: 'video.mp4' });

await mp4.embedFile({ filename: 'secret.pdf' });

const writable = new Writable({ filename: 'output.mp4' });
await mp4.embed(writable);

Embed without encryption

const mp4 = new MP4();
await mp4.loadFile({ filename: 'video.mp4' });

await mp4.embedFile({ filename: 'document.txt' });

const writable = new Writable({ filename: 'output.mp4' });
await mp4.embed(writable);

Mix public and encrypted files

const mp4 = new MP4();
mp4.setKey(key);
await mp4.loadFile({ filename: 'video.mp4' });

// Public file (opt out of encryption with password: null)
await mp4.embedFile({ filename: 'readme.txt', password: null });

// Encrypted file (uses the key set on the MP4 instance)
await mp4.embedFile({ filename: 'secret.pdf' });

const writable = new Writable({ filename: 'output.mp4' });
await mp4.embed(writable);

Embed with metadata

await mp4.embedFile({
  filename: 'photo.jpg',
  meta: { author: 'alice', tags: ['vacation', '2026'] },
});

Extract files

import { MP4, Convert, Writable } from '@h3l1os/mp4vault';

const key = Convert.hexStringToBuffer('000102030405060708090a0b0c0d0e0f');

const mp4 = new MP4();
mp4.setKey(key);
await mp4.loadFile({ filename: 'output.mp4' });

// List embedded files
const files = mp4.getEmbedFiles();
console.log(files);
// [{ filename: 'secret.pdf', size: 12345, isEncrypted: true, offset: 0 }]

// Extract by index
const extracted = await mp4.extractFile(0);
await (extracted as Writable).saveToFile('restored-secret.pdf');

Get expected output size

const mp4 = new MP4();
mp4.setKey(key);
await mp4.loadFile({ filename: 'video.mp4' });
await mp4.embedFile({ filename: 'secret.pdf' });

const expectedBytes = await mp4.getExpectedSize();

API

MP4

| Method | Description | |--------|-------------| | setKey(key: Buffer) | Set AES encryption key (16, 24, or 32 bytes) | | setPassword(password: string) | Set password for PBKDF2 key derivation | | loadFile({ filename }) | Parse an MP4 file | | embedFile({ filename, meta?, key?, password? }) | Add a file to embed. Pass password: null or key: null to opt out of encryption for this file | | embed(writable?) | Write the MP4 with embedded data | | getExpectedSize() | Get the expected output size in bytes | | getEmbedFiles() | List files embedded in the loaded MP4 | | extractFile(index, writable?) | Extract an embedded file by index | | findAtom(name) | Find a top-level atom by name | | findAtoms(atoms, name) | Recursively find atoms by name |

Convert

| Method | Description | |--------|-------------| | hexStringToBuffer(hex) | Convert a hex string to a Buffer (validates input) | | objectToBuffer(obj) | Serialize an object to a Buffer (JSON) | | bufferToObject(buf) | Deserialize a Buffer to an object |

Writable

| Method | Description | |--------|-------------| | new Writable({ filename? }) | Create a writable (file or in-memory) | | saveToFile(filename) | Save in-memory contents to a file | | toReadable() | Convert to a Readable for re-processing | | size() | Get bytes written |

Security

  • AES-256-GCM authenticated encryption (detects tampering)
  • PBKDF2 key derivation with 600,000 iterations and SHA-512 for password-based encryption
  • Random 12-byte IV and 16-byte salt per encryption operation via crypto.randomBytes()
  • Uses Node.js native crypto module — no third-party crypto dependencies

Binary format

Embedded data is placed at the start of the mdat atom payload:

[public header][encrypted header][file1 data][file2 data]...

Each header and file follows this format:

  • Unencrypted: [flag 1B][type 1B][payload]
  • Encrypted: [flag 1B][type 1B][salt 16B][IV 12B][ciphertext][authTag 16B]

Sample offsets in stco/co64 atoms are adjusted to account for the inserted data.

Development

npm install
npm run build        # Build ESM + CJS with tsup
npm run typecheck    # TypeScript strict mode check
npm test             # Run all tests with vitest
npm run test:watch   # Watch mode

Test suite

  • 32 tests across 7 test files
  • Unit tests: AES encryption, Pack binary operations, Convert utilities
  • Integration tests: EmbedBinary, EmbedObject, EmbedMeta
  • End-to-end tests: full embed/extract cycles with key, password, no encryption, mixed files, binary files, metadata, re-embedding, size validation, wrong key rejection

Project structure

src/
  index.ts          # Public API exports
  MP4.ts            # MP4 parser, embedder, extractor
  Atom.ts           # MP4 atom/box representation
  AES.ts            # AES-256-GCM encryption
  Embed.ts          # Embedding coordinator
  EmbedBinary.ts    # Binary file embedding
  EmbedObject.ts    # JSON object embedding
  Convert.ts        # Buffer/hex/JSON utilities
  Pack.ts           # Binary pack/unpack (struct encoding)
  jspack.js         # Vendored jspack library
  constants.ts      # Buffer size, max header, max int32
  types.ts          # IReadable, IWritable, FileRecord interfaces
  utils.ts          # Temp file helper
  node/
    Readable.ts     # Node.js file reader
    Writable.ts     # Node.js file writer
test/
  common.test.ts    # AES, Convert, Pack unit tests
  embedBinary.test.ts
  embedObject.test.ts
  embedMeta.test.ts
  mixed.test.ts
  mp4.test.ts
  e2e.test.ts       # End-to-end embed/extract tests

License

AGPL-3.0