Repository navigation
Security Model
What JAZMIN protects, how, and what it deliberately doesn't. For day-to-day key handling see Encryption and Keys; for per-person access, Access Control.
Only standard algorithms, available natively in browsers (WebCrypto), Node.js and .NET. No custom cryptography.
| Purpose | Algorithm |
|---|---|
| Authenticated encryption of every section | AES-256-GCM, 96-bit random nonce, 128-bit tag |
| Keys per section, from a key | HKDF-SHA256 |
| Key from a password | PBKDF2-HMAC-SHA256, 600,000 iterations by default (readers refuse below 1,000 and above 10,000,000) |
| Owner signatures on shared files | ECDSA P-256 with SHA-256 |
| Integrity of plain (unencrypted) files | CRC-32: accidental damage only |
flowchart TD
K["Key (jzk1-...) or password"] -->|HKDF / PBKDF2| M[Master key]
M -->|HKDF + file salt| HK[Header key]
HK --> H["Header: tables, columns, metadata,<br/>directories, keyring (3 random secrets)"]
M -->|"HKDF + keyring secret + section id"| SK["A unique key for every<br/>chunk, index and file section"]
- Every section has its own key, derived from the master key and a random secret stored in the encrypted header.
- Every ciphertext is bound to its file id, its codec and length, and its section's identity. Moving, swapping or copying sections between files, changing the codec byte, or stripping encryption all fail authentication.
- A new version of a file (update, compact, rotation) gets a new file id, salt and secrets.
-
Keys carry a checksum: a mistyped
jzk1-orjza1-key is reported as a typo, not as wrong data.
An access-controlled file has an owner key and access keys:
- Each file version has fresh random secrets; every partition and every column group gets a secret derived from them. A chunk of partition p and column group g needs both secrets to open.
- Each access key has a key slot holding only the secrets it was granted. From those, the owner secret and all other secrets can't be computed. Partition ids are keyed by the owner secret, so they reveal nothing about partition names.
- The owner signs the key-slot list and header with an ECDSA key derived from the owner key. Each access key carries the owner's fingerprint, so a key holder can't produce a file that verifies as the owner's, and readers reject changed files.
- Indexes are owner-only, because an index would reveal every partition's values.
-
Online grants seal the key slot with the access key and a random share that only the owner's side holds. The
share is the unlock token (
jzu1-...), which the key service issues only before the expiry, so the file can't be opened without asking it. The key service needs only the shares, not the owner key.
| In an encrypted file | Protected? |
|---|---|
| Row values, column names and types, metadata, indexes, embedded files and their names | ✔ encrypted and authenticated |
| Any change to the bytes | ✔ detected |
| A wrong key | ✔ refused (Decryption failed), never misread |
| A plain file passed off as encrypted (downgrade) | ✔ refused when a key is given (A key was supplied but the file is not encrypted) |
| The total size, number and sizes of sections | ✘ visible |
| Whether a password was used, and the PBKDF2 iteration count | ✘ visible |
| Rough row counts | ✘ can be inferred from sizes |
In shared files, every key holder can also see: the default column group's names and types; the names and column counts of restricted groups; file metadata; the number of partitions (as opaque ids) and of key slots; the package settings (entry page and title); the ids of deleted rows. Don't put secrets in metadata or default-group column names.
- Revocation applies to new versions. Someone who kept a copy of an old version, and their old key, can still read what that key was granted in that copy.
- Offline expiry relies on the reader. It catches clocks set back, but someone who deletes their last-seen record and winds the clock back, or runs modified software, can still open a copy they hold. Use online keys when it matters. See Time-Limited Access.
-
The owner key is everything. It reads all, changes the file, grants and revokes, and can't be revoked itself.
Keep it on servers you control, in a secret manager. Browsers refuse it. If it leaks,
rotateOwnerKeyissues a new owner key and new access keys. - A signature proves who produced a file, not who saw it. Anyone can copy data they're allowed to see into a file of their own; it just won't verify as the owner's.
-
Appended files can be rolled back to an earlier version by cutting them at an earlier trailer: that version was
signed by the owner too. If you need to know a file is the latest, record its
appendCountor length elsewhere. -
Compression can leak through sizes when attacker-chosen text shares a section with secrets and the attacker sees
the file size. Use
codec: 'none'for such data. - Plain files have CRC checks only: they catch accidents, not tampering.
Readers treat every file as untrusted: declared lengths and counts are never trusted, memory is bounded by each section's recorded size, PBKDF2 iteration counts outside 1,000–10,000,000 are refused before any work, and unknown flags or features are refused. Both libraries have fuzz tests that feed the readers damaged and hostile input, and a long fuzz run (hours, on both libraries) is part of the checks before a release.
Embedded documents are untrusted content. The viewer runs a package's pages in a sandbox with no network (beyond the
package's allowed https:// origins), no storage and no access to the key; servers serve pages and SVG with a strict
content security policy and a sandbox.
Scanners such as Socket list what the npm package does. These are expected:
-
Code generation (
new Function): the Node reader builds each row with one generated object literal, 2–3× faster than without. Only column names (as JSON strings) and integers go into the generated code; with code generation disabled, the reader falls back to building rows without it. The browser module generates no code. -
Environment variables: only
LOCALAPPDATAon Windows (where expiring keys keep their last-seen record); the CLI also readsJAZMIN_KEYandJAZMIN_PASSWORD. -
File system:
.jzmfiles, lock files and last-seen records. -
No network requests, except the browser module's
openUrl, to the address it's given.
Please report security problems privately, as described in the repository's SECURITY.md, not in a public issue. Include what is affected (library and version, viewer, or format), how to reproduce it, and what an attacker could achieve.
JAZMIN 1.2 · file format 1.0 · Repository · npm · NuGet · MIT license · Examples in this wiki are run and checked by tools/check-examples.mjs
Start
Using files
- Writing Files
- Nested Columns
- Reading and Querying
- Filter Language
- .NET Serializer and LINQ
- Updating and Appending
- Several Tables
- Converting JSON, CSV, XML
- Export Shapes
Security and sharing
- Encryption and Keys
- Access Control
- Masked Columns
- Time-Limited Access
- Sending Records Back
- Security Model
Documents and apps
Performance
Reference