Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Engyon: enprot
Enprot is a confidentiality processor for text and source code files. It lets you embed encrypted, stored, and authenticated segments directly inside any text-based file — source code, documentation, configuration — without breaking the host language's syntax.
- Human-editable markup: EPT directives sit inside host-language comments, so files stay valid C, Rust, Python, Markdown, HTML, or LaTeX.
- Content-addressed storage (CAS): segments can be sanitized out of the document and stored by their SHA3-256 hash. Same content always produces the same CAS file — deterministic, dedup-friendly.
- Authenticated encryption: AES-256-SIV (default), AES-256-GCM, or
AES-256-GCM-SIV. Password-derived keys via Argon2, Scrypt, or PBKDF2.
ML-KEM-based multi-recipient encryption via
--recipient. - Deterministic AEAD: optional
aes-256-gcm-det/aes-256-gcm-siv-detvariants derive the nonce from the plaintext so identical content always produces identical ciphertext — enabling CAS dedup on encrypted segments. - Chain anchors: signed
CHAINblocks provide tamper-evident file history. Multi-signer anchors require N parties to sign. - Merge-friendly: WORD-region merge driver emits
CONFLICTblocks that keep the file valid host-language source.enprot resolveclears them. CAS-referenced blocks deduplicate across branches. - Post-quantum ready: ML-DSA signatures, ML-KEM key encapsulation, and composite Ed25519+ML-DSA constructions ship today.
Installation
From crates.io
From source
Enprot requires Botan 3.x
(brew install botan on macOS; see ci/install.sh for Linux).
or build locally:
Shell completions
After installation, generate completions for your shell:
|
Quick start
EPT markup
An EPT file is a normal text file containing directives hidden inside
host-language comments. The default separators are // <( (left) and
)> (right), which work for C, C++, Rust, JavaScript, and other
//-comment languages.
A simple example (sample/test.ept):
hello, this is a test file
// <( BEGIN GEHEIM )>
Secret line 1
Secret line 2
// <( BEGIN Agent_007 )>
James Bond
// <( END Agent_007 )>
// <( END GEHEIM )>
// <( BEGIN Agent_007 )>
Super secret line 3
// <( END Agent_007 )>
Directive types:
| Directive | Meaning |
|---|---|
BEGIN <WORD> |
Opens a named segment. Segments can nest. |
END <WORD> |
Closes the matching segment. |
STORED <WORD> <hash> |
A CAS pointer — the segment's content lives in the CAS file named <hash>. |
ENCRYPTED <WORD> [hash] [pbkdf:… cipher:…] |
An encrypted segment. If a hash is present, the ciphertext is in CAS; otherwise inline DATA lines follow. |
DATA <base64> |
One or more base64-encoded ciphertext lines inside an ENCRYPTED block. |
CHAIN key:val … |
Signed chain anchor — tamper-evident history. |
INCLUDE <hash> |
Cross-file CAS reference (provenance manifests). |
CONFLICT <WORD> |
Merge-driver conflict marker (resolved by enprot resolve). |
Host-language separators
Use -l/-r to override the separators for non-// languages, or use
the --lang preset:
--lang |
Left sep | Right sep |
|---|---|---|
raw |
<( |
)> |
c |
// <( |
)> |
python |
# <( |
)> |
html |
<!-- <( |
)> --> |
latex |
% <( |
)> |
Example: encrypt a Python file's segments with # <( comments:
Subcommands
encrypt
Encrypt WORD segments inline (ciphertext stays in the document):
If no -k is given, you are prompted interactively (with repeat).
Encrypt-specific options (run enprot encrypt --help for the full list):
| Option | Description |
|---|---|
--cipher <ALG> |
aes-256-siv (default), aes-256-gcm, aes-256-gcm-siv, aes-256-gcm-det, aes-256-gcm-siv-det |
--pbkdf <ALG> |
argon2 (default), scrypt, pbkdf2-sha256, pbkdf2-sha512, legacy |
--pbkdf-msec <MSEC> |
Time-budget for KDF parameter tuning (default 100) |
--pbkdf-salt-len <N> |
Salt length in bytes (default 16) |
--pbkdf-params <K=V,…> |
Override KDF parameters manually (testing) |
--pbkdf-salt <HEX> |
Fixed salt (testing) |
--pbkdf-disable-cache |
Disable PBKDF cache (slower, but no shared derivation state) |
--cipher-iv <HEX> |
Fixed IV (testing) |
--recipient <PUB.pem> |
ML-KEM multi-recipient encryption (repeatable) |
--inline |
Force inline DATA blocks instead of CAS-referenced |
decrypt
Decrypt WORD segments:
Multiple WORDs are comma-separated or repeated. Multiple -k flags
supply passwords for different WORDs.
For KEM-mode blocks (encrypted with --recipient):
store
Sanitize WORD segments to CAS — replace the segment content with a
STORED <WORD> <hash> pointer and write the original content to a file
named by its SHA3-256 hash:
After storing, the CAS directory contains the content:
$ ls cas
cea67c3ef34ff899793b557e9178c1b97bbcfe9722df2f6d35d2d0c91d2c1fe4
The same content always produces the same hash, so storing is idempotent — re-storing identical content is a no-op.
fetch
Restore WORD segments from CAS:
encrypt-store
Encrypt and store in one step. The ciphertext goes to CAS and is referenced by hash:
verify
Check file integrity without decrypting. Validates markup structure, CAS pointer resolution (file exists + hash matches), and extfield formatting:
Output is OK or FAIL per file. Exits non-zero on any failure —
useful in CI pipelines.
list
List all WORD segments in a file with their type and crypto metadata:
For multiple files, each is headed with == <path> ==.
passthrough
Parse and re-write a file without applying any transform. Useful for validating markup or measuring parse performance:
completions
Print a shell completion script:
|
inspect
Combined diagnostic: show structure + chain anchors + conflicts + capabilities in one pass (exit non-zero if conflicts remain):
keygen / sign / verify-sig
Detached signatures (Ed25519, ML-DSA, and composite constructions):
# Generate a keypair (PEM, one file each).
# Sign a file → produces FILE.sig (raw 64-byte Ed25519 signature).
# Verify → reads document.txt.sig by default.
Multi-signer bundles (repeat --key-file):
Override the signature path with --sig-file PATH (verify-sig) or
-o PATH (sign). Both commands read stdin when no FILE is given.
Chain anchors
Signed chain anchors provide tamper-evident file history:
# Produce an anchor after a transform.
# Verify anchors against a trusted pubkey.
# Print the current chain head hash (publish out-of-band).
# Verify against a published pin.
# List unresolved conflicts (exit non-zero if any).
Merge driver + conflict resolution
# Git merge-driver contract (%O=ancestor, %A=ours, %B=theirs, %P=path).
# Resolve conflicts (one mode for all, or per-WORD overrides).
Git filters
# stdin → stdout pipes for git filter/textconv integration.
Provenance + supply chain
# SLSA-style provenance manifest.
# Supply-chain manifest with Cargo.toml dep parsing.
Common options
These flags work with every subcommand (before or after the subcommand name):
| Flag | Description |
|---|---|
-v, --verbose |
Show parse/transform/write progress on stderr |
-q, --quiet |
Suppress non-essential output |
-k, --key <WORD=PASSWORD> |
Supply a password (repeatable; one pair per flag) |
-c, --casdir <DIR> |
CAS directory (default ./cas if it exists, else .) |
-l, --left-separator <SEP> |
Left EPT separator (default // <() |
-r, --right-separator <SEP> |
Right EPT separator (default )>) |
--lang <LANG> |
Preset separators: raw, c, python, html, latex |
--policy <POLICY> |
Crypto policy: default or nist |
--defaults <POLICY> |
Load a policy's defaults without enforcing it |
--fips |
Force FIPS-compliant algorithms (implies --policy=nist) |
--max-depth <N> |
Maximum nesting depth (default 100; 0 = infinite) |
-w, --word <WORD> |
WORD(s) to operate on (repeatable, also comma-separated) |
-o, --output <FILE> |
Output file for the previous input (repeatable) |
-p, --prefix <PREFIX> |
Prefix for output filenames (directory mode if ends with /) |
--output-dir <DIR> |
Write outputs into DIR by basename (conflicts with -p) |
--format <text|json> |
Output format for inspection subcommands |
--policy-file <PATH> |
Capability policy TOML file |
--anchor |
Append a signed chain anchor after transform |
--inline |
Force inline DATA blocks instead of CAS-referenced |
--pbkdf-disable-cache |
Disable PBKDF cache |
Deterministic encryption
By default, AES-GCM and AES-GCM-SIV use random nonces — encrypting the same plaintext twice produces different ciphertexts. This breaks CAS deduplication for encrypted segments.
The -det cipher variants derive the nonce from the plaintext via
HKDF + HMAC, making encryption fully deterministic:
| Cipher | Construction |
|---|---|
aes-256-gcm-det |
enc_key = HKDF(master, "enc"), iv = HMAC(iv_key, pt)[..12], then AES-256-GCM |
aes-256-gcm-siv-det |
Same HKDF/HMAC construction, then AES-256-GCM-SIV (misuse-resistant) |
Same (password, plaintext) always produces the same ciphertext, so
encrypt-store on identical segments deduplicates in CAS.
Usage:
Multi-file processing
Process wildcards in one invocation. Passwords are prompted once:
Output to a different file:
Output to a directory (basename preserved):
# or equivalently:
Plain prefix (prepended verbatim — legacy behavior):
# produces: build_src/main.ept, build_src/lib.ept, …
Working on source code
EPT directives inside comments don't break compilation. Enprot's own
source has an encrypted AUTHOR block in src/lib.rs:
// <( ENCRYPTED AUTHOR )>
// <( DATA X417HVMRRAs6Z1xGo5yY4TxUQ2tpAHEKQ1sg9+kfku5uUikK3y2tODtsUiGqfRGW )>
// <( DATA xUCGYFu02BCdqPM7uuX5UNvbfrLvKkj6gLYwg/cr42PJmr4o5xnw1qo= )>
// <( END AUTHOR )>
Decrypt it:
Crypto policies
Two built-in policies control which algorithms are permitted:
default |
nist |
|
|---|---|---|
| Default cipher | AES-256-SIV | AES-256-GCM |
| Default PBKDF | Argon2 | PBKDF2-SHA-512 |
| Allowed hashes | Any | SHA3-256, SHA3-512 |
| Min salt length | None | 16 bytes |
| Min iterations | None | 1000 |
| GCM IV width | Any | 96 bits |
--fips forces the nist policy. On Linux, it also auto-engages
when /proc/sys/crypto/fips_enabled reads 1.
See docs/fips.adoc for details on what a fully FIPS-validated build
would require.
Compatibility
Documents encrypted by enprot <=0.3.1 still decrypt. The wire format of
the pbkdf: and cipher: extended fields is unchanged:
// <( ENCRYPTED <keyword> [cas-hash] pbkdf:$<id>$<k=v,k=v>$<base64-salt> cipher:$<alg>$iv=<base64-iv> )>
When the ciphertext is in CAS rather than inline, the CAS hash replaces
the inner DATA lines and the cipher: field carries the IV.
The legacy PBKDF (plain SHA3-512 truncation) is retained for
decrypting old blobs; selecting it for new encryption prints a warning.
See docs/migration-0.3-to-0.4.md for the full migration guide
(subcommand CLI, CAS-referenced default, CONFLICT directive, chain
anchors, feature-gated library).
Configuration
enprot init writes .enprot.toml. Layered resolution:
- Built-in defaults
~/.config/enprot/config.toml(user global).enprot.toml(project local, walked up from cwd)ENPROPT_*environment variables- CLI flags (highest precedence)
= "cas"
= "c"
[]
= "aes-256-gcm-siv-det"
= "argon2"
[]
# signer = "confium://session-id"
# auto_anchor = true
Library usage
Enprot can be used as a Rust library without the CLI:
[]
= { = "0.4", = false }
The default-features = false build excludes clap and clap_complete.
All library modules (crypto, parsing, capability model, chain anchors,
Merkle trees, merge driver, provenance, SCM) are available.
Development
See CONTRIBUTING.md for the full guide. Quick reference:
&&
Architecture docs: CLAUDE.md. Wire format specs: docs/schemas/.
Upgrade history: TODO.upgrade/, TODO.audit/, TODO.issues/,
TODO.finalize/, TODO.roadmap/.
License
BSD-2-Clause. See LICENSE file.