README · Architecture · Security · Contributing · Releasing
Thank you for your interest in contributing to zefer-cli. Please read the Code of Conduct before participating.
- Node.js 20+
- npm 10+
git clone https://github.com/carrilloapps/zefer-cli.git
cd zefer-cli
npm installnpm run dev # Run with tsx (no build step needed)
npm run build # Compile to dist/
npm run typecheck # TypeScript strict check (no errors expected)# Build
npm run build
# Encrypt a file
node dist/index.js encrypt <your-file> -p testpass
# Decrypt it back
node dist/index.js decrypt <your-file>.zefer -p testpass --force
# Verify output matches the originalsrc/
commands/ # One file per CLI command (Ink React components)
lib/ # Core crypto libraries (Node.js ports of zefer/app/lib/)
ui/ # Reusable Ink components (Header, ProgressBar, Spinner, StatusLine)
utils/ # Shared utilities (format, prompt, terminal detection)
index.ts # CLI entry point (Commander setup)
- Strict mode — no
any, noascasts without a comment explaining why - Async functions for all I/O and crypto operations
Buffer(notUint8Array) for binary data at the boundary between Node.js and the zefer format>>> 0after bitwise XOR operations that may produce signed results
- Each command (
encrypt,decrypt,keygen,info) is a React component exported asXxxApp - Props are typed with an
XxxOptionsinterface in the same file - All side effects run in a single
useEffecton mount - Call
useApp().exit()(with a delay) when the operation completes - Use
StatusLinefor the standard spinner + progress bar UI
- Never use hard-coded Unicode characters — use
src/utils/terminal.tsexports (ICON_OK,BAR_FILLED, etc.) - Colors via Ink's
<Text color="...">— never via chalk in React components - chalk is only used for non-Ink output (if any)
- Test password prompts with and without a TTY (pipe a passphrase via stdin)
- Verify
ZEFER_ASCII=1produces sensible output - File paths: use
path.join/path.basename, never string concatenation
The .zefer binary format is shared with the web app. Do not change the format without also updating zefer and incrementing the magic bytes (e.g., ZEFB4). Cross-compatibility is a hard requirement.
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Make your changes
- Run
npm run typecheck— zero errors required - Run
npm run build— must succeed - Test manually: encrypt a file, decrypt it, verify the output
- Submit a pull request with a clear description
- Create
src/commands/<name>.tsxwith anXxxAppcomponent andXxxOptionsinterface - Wire it in
src/index.tsusing Commander's.command()API - Export it from the command file
- Add it to the README command reference
Security options always live inside the encrypted payload (ZeferMeta), never in the public header (ZeferHeader). To add a new option:
- Add the field to
ZeferMetainsrc/lib/zefer.ts - Add it to
EncodeOptionsandencodeZefer() - Add the post-decryption check in
decodeZefer() - Add the CLI flag in
src/index.ts - Add it to
src/commands/encrypt.tsxprops and--verbosedisplay - Update
docs/ARCHITECTURE.mdandREADME.md - If it's also a new format version, update the web app at the same time
Key generation lives in src/lib/passwords.ts (shared by the CLI, the MCP
server and the library).
- Add the mode to the
KeygenModeunion insrc/lib/passwords.ts - Add its alphabet to
CHARSETSand an entry toMODES - Verify
generateValue()/generateWithOptions()handle it - Add it to the
--modeoption description insrc/index.ts(CLI) and to themodeenum in thezefer_keygentool insrc/mcp/server.ts(MCP) - Update the README keygen modes table
Publishing is automated via GitHub Actions. See the full process in docs/RELEASING.md.
Quick summary:
npm version patch|minor|major- Update
CHANGELOG.md, thengit commit --amend --no-edit git push origin main --tagsgh release create v<version> --generate-notes- GitHub Actions publishes to npm automatically
If your change touches cryptographic code (src/lib/crypto.ts, src/lib/chunked-crypto.ts, src/lib/zefer.ts):
- Verify byte-for-byte compatibility with the web app by encrypting with the CLI and decrypting in the browser (and vice versa)
- Do not change iteration counts, salt lengths, IV lengths, or auth tag handling without a documented reason
- If you find a security issue, please follow the Security Policy instead of opening a public PR
By contributing, you agree that your contributions will be licensed under the MIT License.