Skip to content

Latest commit

 

History

History
172 lines (125 loc) · 6.69 KB

File metadata and controls

172 lines (125 loc) · 6.69 KB

Contributing

README · Architecture · Security · Contributing · Releasing

Thank you for your interest in contributing to zefer-cli. Please read the Code of Conduct before participating.

Prerequisites

  • Node.js 20+
  • npm 10+

Setup

git clone https://github.com/carrilloapps/zefer-cli.git
cd zefer-cli
npm install

Development

npm run dev          # Run with tsx (no build step needed)
npm run build        # Compile to dist/
npm run typecheck    # TypeScript strict check (no errors expected)

Test your changes

Two suites, both required before opening a pull request:

npm test                 # Vitest unit suite (src/**/__tests__)
npm run test:watch       # the same suite in watch mode
npm run test:coverage    # unit suite + coverage gate
npm run test:integration # scripts/test.mjs — 96 checks across CLI, MCP and library
npm run verify           # lint + typecheck + build + both suites

Coverage is enforced at 100% for statements, branches, functions and lines over all of src/** (vitest.config.ts). Lowering a threshold, excluding a file or adding a v8 ignore hint is not an accepted way to make the gate pass: write the missing test, or delete the unreachable code.

Unit tests live next to the code they cover, in __tests__ directories. Ink components are rendered with ink-testing-library; the MCP server is driven through its real stdio JSON-RPC loop over a fake stdin/stdout pair.

The integration suite (scripts/test.mjs) is deliberately kept separate: it builds the package and exercises the shipped binary, the MCP server and the published library, including cross-channel compatibility. Coverage is not measured there.

You can also try a change by hand:

npm run build
node dist/index.js encrypt <your-file> -p testpass
node dist/index.js decrypt <your-file>.zefer -p testpass --force
# Verify the output matches the original

Project Structure

src/
  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, path safety, terminal detection)
  mcp/            # MCP stdio server (JSON-RPC over stdin/stdout)
  lib.ts          # Programmatic library entry point (ESM + CJS)
  index.ts        # CLI entry point (Commander setup)
  **/__tests__/   # Vitest unit suites, alongside the code they cover

Conventions

TypeScript

  • Strict mode — no any, no as casts without a comment explaining why
  • Async functions for all I/O and crypto operations
  • Buffer (not Uint8Array) for binary data at the boundary between Node.js and the zefer format
  • >>> 0 after bitwise XOR operations that may produce signed results

Ink components

  • Each command (encrypt, decrypt, keygen, info) is a React component exported as XxxApp
  • Props are typed with an XxxOptions interface in the same file
  • All side effects run in a single useEffect on mount
  • Call useApp().exit() (with a delay) when the operation completes
  • Use StatusLine for the standard spinner + progress bar UI

Terminal output

  • Never use hard-coded Unicode characters — use src/utils/terminal.ts exports (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)

Cross-platform

  • Test password prompts with and without a TTY (pipe a passphrase via stdin)
  • Verify ZEFER_ASCII=1 produces sensible output
  • File paths: use path.join / path.basename, never string concatenation

Binary format compatibility

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.

Making Changes

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/your-feature
  3. Make your changes
  4. Run npm run typecheck — zero errors required
  5. Run npm run build — must succeed
  6. Test manually: encrypt a file, decrypt it, verify the output
  7. Submit a pull request with a clear description

Adding a New Command

  1. Create src/commands/<name>.tsx with an XxxApp component and XxxOptions interface
  2. Wire it in src/index.ts using Commander's .command() API
  3. Export it from the command file
  4. Add it to the README command reference

Adding a New Security Option

Security options always live inside the encrypted payload (ZeferMeta), never in the public header (ZeferHeader). To add a new option:

  1. Add the field to ZeferMeta in src/lib/zefer.ts
  2. Add it to EncodeOptions and encodeZefer()
  3. Add the post-decryption check in decodeZefer()
  4. Add the CLI flag in src/index.ts
  5. Add it to src/commands/encrypt.tsx props and --verbose display
  6. Update docs/ARCHITECTURE.md and README.md
  7. If it's also a new format version, update the web app at the same time

Adding a New Keygen Mode

Key generation lives in src/lib/passwords.ts (shared by the CLI, the MCP server and the library).

  1. Add the mode to the KeygenMode union in src/lib/passwords.ts
  2. Add its alphabet to CHARSETS and an entry to MODES
  3. Verify generateValue() / generateWithOptions() handle it
  4. Add it to the --mode option description in src/index.ts (CLI) and to the mode enum in the zefer_keygen tool in src/mcp/server.ts (MCP)
  5. Update the README keygen modes table

Releasing a New Version

See the full process in docs/RELEASING.md.

Quick summary:

  1. npm version patch|minor|major
  2. Update CHANGELOG.md, then git commit --amend --no-edit
  3. git push origin main --tags
  4. npm publish --otp=XXXXXX — publishing is manual; there is no npm token in CI
  5. gh release create v<version> --generate-notes — this triggers binaries.yml, which builds and attaches the standalone binaries

Security Contributions

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

License

By contributing, you agree that your contributions will be licensed under the MIT License.