{"_id":"cross-keychain","_rev":"2-b28db4871e6846b36985556534afba9b","name":"cross-keychain","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.1":{"name":"cross-keychain","version":"1.0.1","keywords":["cross-keychain","secrets","storage","encryption","cross-platform","credential locker","secret service","keychain","windows","linux","macos","security"],"author":{"url":"https://magarcia.io/","name":"Martin Garcia","email":"contact@magarcia.io"},"license":"MIT","_id":"cross-keychain@1.0.1","maintainers":[{"name":"magarcia","email":"newluxfero@gmail.com"}],"homepage":"https://github.com/magarcia/cross-keychain#readme","bugs":{"url":"https://github.com/magarcia/cross-keychain/issues"},"bin":{"cross-keychain":"dist/cli.js"},"dist":{"shasum":"db539f15c1a9c4f6ac923ec11529964d9bbb2997","tarball":"https://registry.npmjs.org/cross-keychain/-/cross-keychain-1.0.1.tgz","fileCount":12,"integrity":"sha512-jas8ZuYHw02e0tZLyN7X93yHwWlsey0G43s4O37kX4yYc2UDocJCc7+MVwNYr+OrA8ENGzkMiy07/zGfOmBtAg==","signatures":[{"sig":"MEUCIQC2ylE38Io795AzNaR+dIIKTajYB20A8nIM51BXy2KyqgIgeAei2TWY4xk92NMrNp9Unz8DL7AKUX7CD5u5t6w2L6E=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/cross-keychain@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":267845},"main":"dist/index.cjs","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"b86b3759378bdbd60df94557f7901d2bf7409163","scripts":{"ci":"npm run lint && npm run typecheck && npm run test && npm run build","lint":"eslint .","test":"vitest run","build":"rollup -c rollup.config.js","clean":"rm -rf dist","start":"node ./dist/index.js","format":"prettier --write .","prepare":"husky","prepush":"npm run test && npm run typecheck && npm run build","release":"semantic-release","coverage":"vitest run --coverage","lint:fix":"eslint . --fix","security":"npm audit --audit-level=moderate","precommit":"lint-staged","typecheck":"tsc --noEmit","deps:check":"npm-check-updates","test:watch":"vitest watch","deps:unused":"knip","deps:update":"npm-check-updates -u","release:dry":"semantic-release --dry-run","format:check":"prettier --check ."},"_npmUser":{"name":"magarcia","email":"newluxfero@gmail.com"},"repository":{"url":"git+https://github.com/magarcia/cross-keychain.git","type":"git"},"_npmVersion":"10.9.3","description":"Cross-platform secret storage","directories":{},"lint-staged":{"*.{css,scss,less}":["prettier --write"],"*.{json,md,yml,yaml}":["prettier --write"],"*.{js,ts,jsx,tsx,mjs,cjs}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"20.19.5","dependencies":{"meow":"^14.0.0","@inquirer/prompts":"^7.8.6"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"knip":"^5.64.1","husky":"^9.1.7","tslib":"^2.8.1","eslint":"^9.36.0","rollup":"^4.52.3","vitest":"^3.2.4","globals":"^16.4.0","prettier":"^3.6.2","typescript":"^5.9.2","@types/node":"^24.6.0","lint-staged":"^16.2.3","@commitlint/cli":"^20.0.0","semantic-release":"^24.2.9","npm-check-updates":"^19.0.0","rollup-plugin-dts":"^6.2.3","typescript-eslint":"^8.45.0","rollup-plugin-copy":"^3.5.0","@rollup/plugin-json":"^6.1.0","eslint-plugin-jsdoc":"^60.5.0","@semantic-release/git":"^10.0.1","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4","@rollup/plugin-commonjs":"^28.0.6","@semantic-release/github":"^11.0.6","@rollup/plugin-typescript":"^12.1.4","@typescript-eslint/parser":"^8.45.0","@vitest/coverage-istanbul":"^3.2.4","@rollup/plugin-node-resolve":"^16.0.1","@semantic-release/changelog":"^6.0.3","@commitlint/config-conventional":"^20.0.0","@typescript-eslint/eslint-plugin":"^8.45.0"},"optionalDependencies":{"@napi-rs/keyring":"^1.2.0"},"_npmOperationalInternal":{"tmp":"tmp/cross-keychain_1.0.1_1759234278942_0.45414302017281893","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"cross-keychain","version":"1.1.0","description":"Cross-platform secret storage","author":{"name":"Martin Garcia","email":"contact@magarcia.io","url":"https://magarcia.io/"},"license":"MIT","keywords":["cross-keychain","secrets","storage","encryption","cross-platform","credential locker","secret service","keychain","windows","linux","macos","security"],"type":"module","main":"dist/index.cjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"bin":{"cross-keychain":"dist/cli.js"},"engines":{"node":">=18"},"sideEffects":false,"publishConfig":{"access":"public","provenance":true},"scripts":{"build":"rollup -c rollup.config.js","typecheck":"tsc --noEmit","clean":"rm -rf dist","start":"node ./dist/index.js","lint":"eslint .","lint:fix":"eslint . --fix","format":"prettier --write .","format:check":"prettier --check .","test":"vitest run","test:watch":"vitest watch","coverage":"vitest run --coverage","prepare":"husky","precommit":"lint-staged","prepush":"npm run test && npm run typecheck && npm run build","release":"semantic-release","release:dry":"semantic-release --dry-run","deps:check":"npm-check-updates","deps:update":"npm-check-updates -u","deps:unused":"knip","security":"npm audit --audit-level=moderate","ci":"npm run lint && npm run typecheck && npm run test && npm run build"},"repository":{"type":"git","url":"git+https://github.com/magarcia/cross-keychain.git"},"bugs":{"url":"https://github.com/magarcia/cross-keychain/issues"},"homepage":"https://github.com/magarcia/cross-keychain#readme","dependencies":{"@inquirer/prompts":"^7.8.6","meow":"^14.0.0"},"optionalDependencies":{"@napi-rs/keyring":"^1.2.0"},"devDependencies":{"@commitlint/cli":"^20.1.0","@commitlint/config-conventional":"^20.0.0","@rollup/plugin-commonjs":"^28.0.6","@rollup/plugin-json":"^6.1.0","@rollup/plugin-node-resolve":"^16.0.2","@rollup/plugin-typescript":"^12.1.4","@semantic-release/changelog":"^6.0.3","@semantic-release/git":"^10.0.1","@semantic-release/github":"^11.0.6","@types/node":"^24.7.0","@typescript-eslint/eslint-plugin":"^8.46.0","@typescript-eslint/parser":"^8.46.0","@vitest/coverage-istanbul":"^3.2.4","eslint":"^9.37.0","eslint-config-prettier":"^10.1.8","eslint-plugin-jsdoc":"^60.8.3","eslint-plugin-prettier":"^5.5.4","globals":"^16.4.0","husky":"^9.1.7","knip":"^5.64.2","lint-staged":"^16.2.3","npm-check-updates":"^19.0.0","prettier":"^3.6.2","rollup":"^4.52.4","rollup-plugin-copy":"^3.5.0","rollup-plugin-dts":"^6.2.3","semantic-release":"^24.2.9","tslib":"^2.8.1","typescript":"^5.9.3","typescript-eslint":"^8.46.0","vitest":"^3.2.4"},"lint-staged":{"*.{js,ts,jsx,tsx,mjs,cjs}":["eslint --fix","prettier --write"],"*.{json,md,yml,yaml}":["prettier --write"],"*.{css,scss,less}":["prettier --write"]},"_id":"cross-keychain@1.1.0","gitHead":"26f3d4fcf9165786941011109e17fd7f732dced1","types":"./dist/index.d.ts","_nodeVersion":"20.19.5","_npmVersion":"10.9.4","dist":{"integrity":"sha512-244DWNdGepLKD5vEn3reZqwzZFiE/LD4U+XV9IaXQbtIXKvQkf0VkRaOj/9vPYauPdR12PSGB3U0cE7jJi3WTQ==","shasum":"e3588562aac720fa94685958cf3acdbc42027fa4","tarball":"https://registry.npmjs.org/cross-keychain/-/cross-keychain-1.1.0.tgz","fileCount":12,"unpackedSize":312090,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/cross-keychain@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHprhvq43JDOfpSfgjnT9kP1psxMjRfpLduFXeFZuUhtAiEA7+95I0SiPJWV59wLEQUlmZ+q/PYWKp0aiQp1la8Ss4s="}]},"_npmUser":{"name":"magarcia","email":"newluxfero@gmail.com"},"directories":{},"maintainers":[{"name":"magarcia","email":"newluxfero@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/cross-keychain_1.1.0_1759874162226_0.4963717118424962"},"_hasShrinkwrap":false}},"time":{"created":"2025-09-30T12:11:18.843Z","modified":"2025-10-07T21:56:02.848Z","1.0.1":"2025-09-30T12:11:19.132Z","1.1.0":"2025-10-07T21:56:02.431Z"},"bugs":{"url":"https://github.com/magarcia/cross-keychain/issues"},"author":{"name":"Martin Garcia","email":"contact@magarcia.io","url":"https://magarcia.io/"},"license":"MIT","homepage":"https://github.com/magarcia/cross-keychain#readme","keywords":["cross-keychain","secrets","storage","encryption","cross-platform","credential locker","secret service","keychain","windows","linux","macos","security"],"repository":{"type":"git","url":"git+https://github.com/magarcia/cross-keychain.git"},"description":"Cross-platform secret storage","maintainers":[{"name":"magarcia","email":"newluxfero@gmail.com"}],"readme":"# cross-keychain\n\n[![CI Status](https://github.com/magarcia/cross-keychain/workflows/CI/badge.svg)](https://github.com/magarcia/cross-keychain/actions)\n[![codecov](https://codecov.io/gh/magarcia/cross-keychain/branch/main/graph/badge.svg)](https://codecov.io/gh/magarcia/cross-keychain)\n[![npm version](https://badge.fury.io/js/cross-keychain.svg)](https://www.npmjs.com/package/cross-keychain)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nCross-platform secret storage for Node.js applications and CLI usage.\n\n## Features\n\n- Works across Windows, macOS, and Linux using native credential storage\n- Secure storage using Windows Credential Manager, macOS Keychain, and Linux Secret Service\n- **Native macOS Keychain integration** via Security.framework bindings for enhanced security (no password exposure in process lists)\n- Automatic fallback to CLI-based backends when native modules unavailable\n- Simple CLI interface for managing secrets\n- TypeScript support with full type definitions\n- Programmatic API for Node.js applications\n\n## Installation\n\n```sh\nnpm install cross-keychain\n# or\nyarn add cross-keychain\n# or\npnpm add cross-keychain\n```\n\n## CLI Usage\n\nOnce installed, you can use the `cross-keychain` command to manage secrets:\n\n### Basic Commands\n\n```sh\n# Store a secret (will prompt for password)\ncross-keychain set myapp username\n# Password for 'username' in 'myapp': [hidden input]\n# Password stored\n\n# Store a secret via stdin (non-interactive)\necho \"my-secret\" | cross-keychain set myapp username\n# Password stored\n\n# Retrieve a password\ncross-keychain get myapp username\n# my-secret\n\n# Retrieve credentials as JSON\ncross-keychain get myapp username --mode creds --output json\n# {\"username\":\"username\",\"password\":\"my-secret\"}\n\n# Retrieve any credential for a service\ncross-keychain get myapp --mode creds\n# username\n# my-secret\n\n# Delete a secret\ncross-keychain del myapp username\n# Password deleted\n```\n\n### Advanced Options\n\n```sh\n# List available backends\ncross-keychain --list-backends\n# file\t(priority: 1)\tFile backend\n# keychain\t(priority: 10)\tmacOS Keychain\n\n# Force a specific backend\ncross-keychain get myapp username --backend file\n\n# Disable keyring (use null backend)\ncross-keychain --disable\n# Null backend configured\n\n# Diagnose current configuration\ncross-keychain diagnose\n# {\n#   \"backend\": \"keychain\",\n#   \"available_backends\": [...]\n# }\n```\n\n### Command Reference\n\n**Operations:**\n\n- `get` - Retrieve a password or credential\n- `set` - Store a password (prompts securely or reads from stdin)\n- `del` - Delete a password\n- `diagnose` - Print environment details\n\n**Options:**\n\n- `--backend <id>` - Force a specific backend\n- `--mode <mode>` - Mode for 'get' operation (`password` or `creds`)\n- `--output <format>` - Output format for 'get' operation (`plain` or `json`)\n- `--password-stdin` - Read password from stdin for 'set' operation\n- `--list-backends` - List detected backends\n- `--disable` - Persistently configure the null backend\n\n## Programmatic Usage\n\n```ts\nimport {\n  setPassword,\n  getPassword,\n  deletePassword,\n  getCredential,\n} from \"cross-keychain\";\n\n// Store a secret\nawait setPassword(\"myapp\", \"username\", \"john_doe\");\n\n// Retrieve a secret\nconst password = await getPassword(\"myapp\", \"username\");\nconsole.log(password); // \"john_doe\"\n\n// Delete a secret\nawait deletePassword(\"myapp\", \"username\");\n\n// Get credential for a service and account\nconst credential = await getCredential(\"myapp\", \"username\");\nconsole.log(credential); // { username: \"username\", password: \"john_doe\" }\n\n// Get first available credential for a service\nconst firstCredential = await getCredential(\"myapp\");\nconsole.log(firstCredential); // { username: \"username\", password: \"john_doe\" }\n```\n\n## API\n\n### `setPassword(service, account, password)`\n\n- `service` (`string`): The service name to store the password under\n- `account` (`string`): The account name\n- `password` (`string`): The password to store\n\nStores a password in the system keyring.\n\n### `getPassword(service, account)`\n\n- `service` (`string`): The service name\n- `account` (`string`): The account name\n\nReturns the stored password for the given service and account, or `null` if not found.\n\n### `deletePassword(service, account)`\n\n- `service` (`string`): The service name\n- `account` (`string`): The account name\n\nDeletes the stored password for the given service and account.\n\n### `getCredential(service, account?)`\n\n- `service` (`string`): The service name\n- `account` (`string`, optional): The account name. If not provided, returns the first available credential for the service\n\nReturns a credential object with `username` and `password` properties for the given service and account, or `null` if not found.\n\n**Platform Limitations:**\n\n- **Without account parameter:** Only supported on Linux (Secret Service), file backend, and CLI-based macOS Keychain\n- **Windows and native macOS backends:** Require explicit account parameter\n- Platform limitations are due to underlying credential API constraints\n\n**Note**: If multiple credentials exist for the service, the one returned is not guaranteed to be the same every time.\n\n## Configuration\n\nKeyring supports various configuration methods to customize backend selection and behavior.\n\n### Environment Variables\n\n#### Force Backend Selection\n\n**`TS_KEYRING_BACKEND`** - Forces a specific backend to be used:\n\n```sh\n# Force native macOS Keychain (Security.framework bindings - when available)\nexport TS_KEYRING_BACKEND=native-macos\n\n# Force CLI-based macOS Keychain (security command - when available)\nexport TS_KEYRING_BACKEND=macos\n\n# Force Windows Credential Manager (when available)\nexport TS_KEYRING_BACKEND=windows\n\n# Force Linux Secret Service (when available)\nexport TS_KEYRING_BACKEND=secret-service\n\n# Force the file backend\nexport TS_KEYRING_BACKEND=file\n\n# Force the null backend (disables storage)\nexport TS_KEYRING_BACKEND=null\n```\n\n#### Backend Property Overrides\n\n**`KEYRING_PROPERTY_*`** - Override backend-specific properties:\n\n```sh\n# File backend: Custom storage location\nexport KEYRING_PROPERTY_FILE_PATH=\"/custom/path/secrets.json\"\n\n# macOS backend: Use specific keychain\nexport KEYRING_PROPERTY_KEYCHAIN=\"/path/to/custom.keychain\"\n\n# Linux Secret Service: Custom application identifier\nexport KEYRING_PROPERTY_APPLICATION=\"my-custom-app\"\nexport KEYRING_PROPERTY_APPID=\"my-custom-app\"  # Alternative name\n\n# Linux Secret Service: Use specific collection\nexport KEYRING_PROPERTY_COLLECTION=\"my-collection\"\nexport KEYRING_PROPERTY_PREFERRED_COLLECTION=\"my-collection\"  # Alternative name\n\n# Windows: Set credential persistence level\nexport KEYRING_PROPERTY_PERSIST=\"local\"  # or \"session\", \"enterprise\"\n```\n\n### Configuration File\n\nKeyring uses a JSON configuration file for persistent settings:\n\n**Location:** `keyring.config.json` in the platform-specific config directory:\n\n- **Windows:** `%LOCALAPPDATA%\\Keyring\\keyring.config.json` or `%APPDATA%\\Keyring\\keyring.config.json`\n- **macOS:** `~/.config/keyring/keyring.config.json` (or `$XDG_CONFIG_HOME/keyring/keyring.config.json`)\n- **Linux:** `~/.config/keyring/keyring.config.json` (or `$XDG_CONFIG_HOME/keyring/keyring.config.json`)\n\n**Schema:**\n\n```json\n{\n  \"defaultBackend\": \"file\",\n  \"backendProperties\": {\n    \"file\": {\n      \"file_path\": \"/custom/path/secrets.json\"\n    },\n    \"native-macos\": {\n      \"keychain\": \"/path/to/custom.keychain\"\n    },\n    \"macos\": {\n      \"keychain\": \"/path/to/custom.keychain\"\n    },\n    \"secret-service\": {\n      \"application\": \"my-app\",\n      \"collection\": \"my-collection\"\n    },\n    \"windows\": {\n      \"persist\": \"local\"\n    }\n  }\n}\n```\n\n**Example configurations:**\n\n```json\n// Disable keyring (use null backend)\n{\n  \"defaultBackend\": \"null\"\n}\n\n// Use file backend with custom location\n{\n  \"defaultBackend\": \"file\",\n  \"backendProperties\": {\n    \"file\": {\n      \"file_path\": \"/secure/vault/secrets.json\"\n    }\n  }\n}\n\n// Use Windows Credential Manager with session persistence\n{\n  \"defaultBackend\": \"windows\",\n  \"backendProperties\": {\n    \"windows\": {\n      \"persist\": \"session\"\n    }\n  }\n}\n```\n\n### Backend-Specific Configuration\n\n#### File Backend Properties\n\n- **`file_path`** (`string`): Custom path for the secrets JSON file\n  - Default: `{dataRoot}/secrets.json`\n  - Example: `\"/custom/path/secrets.json\"`\n\n- **`key_file_path`** (`string`): Custom path for the encryption key file\n  - Default: `{configRoot}/file.key`\n  - Example: `\"/custom/path/file.key\"`\n  - **Environment variable:** `KEYRING_FILE_MASTER_KEY` - 64 hex character (32 byte) key to override file-based key\n\n#### Native macOS Keychain Backend Properties\n\n- **`keychain`** (`string`): Path to a specific keychain file\n  - Default: Uses the default keychain\n  - Example: `\"/path/to/custom.keychain\"`\n  - **Note**: Requires @napi-rs/keyring optional dependency to be installed\n\n#### macOS Keychain Backend (CLI) Properties\n\n- **`keychain`** (`string`): Path to a specific keychain file\n  - Default: Uses the default keychain\n  - Example: `\"/path/to/custom.keychain\"`\n  - **Note**: This is the CLI-based fallback when native bindings are unavailable\n\n#### Linux Secret Service Backend Properties\n\n- **`application`** / **`appid`** (`string`): Application identifier for stored secrets\n  - Default: `\"ts-keyring\"`\n  - Example: `\"my-application\"`\n\n- **`collection`** / **`preferred_collection`** (`string`): Specific keyring collection to use\n  - Default: Uses the default collection\n  - Example: `\"my-collection\"`\n\n#### Windows Credential Manager Backend Properties\n\n- **`persist`** (`string` | `number`): Credential persistence level\n  - **`\"session\"` / `1`**: Credentials are deleted when the user logs off\n  - **`\"local\"` / `2`**: Credentials persist until explicitly deleted (default)\n  - **`\"enterprise\"` / `3`**: Credentials roam with the user profile\n  - Custom numeric values are also supported\n\n### disable() Function\n\nThe `disable()` function creates a configuration file that forces the null backend:\n\n```ts\nimport { disable } from \"cross-keychain\";\n\n// Persistently disable keyring\nawait disable();\n```\n\n**Behavior:**\n\n- Creates `keyring.config.json` with `\"defaultBackend\": \"null\"`\n- Throws `KeyringError` if configuration file already exists\n- All subsequent operations will use the null backend (no actual storage)\n- File permissions are set to `0600` (owner read/write only)\n\n**To re-enable keyring:** Delete the configuration file manually\n\n### Configuration Priority\n\nKeyring uses the following priority order for backend selection:\n\n1. **Environment variable:** `TS_KEYRING_BACKEND` (highest priority)\n2. **Configuration file:** `defaultBackend` setting\n3. **Auto-detection:** Based on platform and backend availability (lowest priority)\n\nEnvironment property overrides (`KEYRING_PROPERTY_*`) always take precedence over configuration file settings.\n\n## Platform Support\n\n- **Windows**: Uses native Windows Credential Manager bindings (via @napi-rs/keyring) with automatic fallback to PowerShell-based access\n- **macOS**: Uses native Security.framework bindings (via @napi-rs/keyring) with automatic fallback to CLI-based Keychain Access\n- **Linux**: Uses native Secret Service API bindings (via @napi-rs/keyring) with automatic fallback to secret-tool\n\n### Backend Priority System\n\ncross-keychain uses a priority-based system to automatically select the best available backend:\n\n| Backend                               | Platform | Priority | Method                      | Security                                        |\n| ------------------------------------- | -------- | -------- | --------------------------- | ----------------------------------------------- |\n| Native macOS Keychain                 | macOS    | 10       | Security.framework bindings | ✅ Highest - Direct API access                  |\n| Native Windows Credential Manager     | Windows  | 10       | Native DPAPI bindings       | ✅ Highest - Direct API access                  |\n| Native Linux Secret Service           | Linux    | 10       | Native DBus bindings        | ✅ Highest - Direct API access                  |\n| macOS Keychain (CLI Fallback)         | macOS    | 5        | `security` command          | ✅ High - OS keychain, password in process list |\n| Windows Credential Manager (Fallback) | Windows  | 5        | PowerShell DPAPI            | ✅ High - OS credential manager                 |\n| Linux Secret Service (Fallback)       | Linux    | 4.8      | `secret-tool`               | ✅ High - OS keyring service                    |\n| File Backend                          | All      | 0.5      | Encrypted JSON file         | ⚠️ Limited - AES-256-GCM encrypted, file-based  |\n| Null Backend                          | All      | -1       | No storage                  | ❌ None - Disabled                              |\n\nThe native backends (macOS, Windows, and Linux) use @napi-rs/keyring (installed as an optional dependency) for direct API access through native bindings, providing the highest security and performance. These backends eliminate password exposure in process lists and shell command injection risks that can occur with CLI-based approaches. If the native module is not available, the library automatically falls back to shell-based backends.\n\n## Security Considerations\n\n⚠️ **CRITICAL SECURITY WARNING** ⚠️\n\nThe security of your stored credentials depends entirely on which backend is used. **Always use native OS backends in production environments.**\n\n### Secure Backends (Recommended for Production)\n\nThese backends use your operating system's built-in credential management and provide strong security:\n\n**🔒 macOS Keychain (Native)**\n\n- **Highest security**: Uses Security.framework bindings via @napi-rs/keyring\n- **No password exposure**: Passwords never appear in process lists or command line arguments\n- Hardware-encrypted storage when available (Secure Enclave on newer Macs)\n- Integrates with macOS authentication policies and Touch ID/Face ID\n- Passwords encrypted using your login keychain password\n- Access restricted to your user account only\n- **Automatic fallback**: Falls back to CLI-based keychain if native module unavailable\n\n**🔒 macOS Keychain (CLI - Fallback)**\n\n- Uses `security` command-line tool\n- ⚠️ **Security caveat**: Passwords briefly visible in process lists during operations\n- Same encryption and access controls as native backend\n- Automatically selected when native module cannot be loaded\n\n**🔒 Windows Credential Manager**\n\n- Uses DPAPI (Data Protection API) encryption tied to your user account\n- Integrates with Windows security policies and Windows Hello\n- Automatic encryption/decryption handled by the OS\n- Access restricted to your user account only\n\n**🔒 Linux Secret Service**\n\n- Encrypted storage with master password protection\n- Integrates with GNOME Keyring or KDE Wallet\n- Access restricted to your current user session\n- Supports multiple keyrings and collections\n\n### ⚠️ File Backend - LIMITED SECURITY\n\n**WARNING**: The file backend provides encryption but has significant limitations compared to OS backends.\n\n**Security features:**\n\n- ✅ **AES-256-GCM encryption** with 96-bit IV and authentication tags\n- ✅ **Per-user encryption key** stored in `~/.config/keyring/file.key` (0600 permissions)\n- ✅ **Environment variable override**: Use `KEYRING_FILE_MASTER_KEY` env var for key management\n- ✅ **Atomic writes**: Prevents corruption on crash/interrupt\n- ✅ File and key file permissions set to `0600` (owner read/write only)\n- ✅ Directory permissions set to `0700` (owner access only)\n\n**Threat model - Protects against:**\n\n- ✅ Casual access to the secrets file\n- ✅ File corruption from interrupted writes\n- ✅ Accidental exposure of plaintext secrets\n\n**Security limitations:**\n\n- ⚠️ Encryption key stored on same system as encrypted data\n- ⚠️ Anyone with root/administrator access can read key file and decrypt\n- ⚠️ **NOT a substitute for OS keychain** - lacks hardware security and OS integration\n- ⚠️ Memory is not zeroized (keys may remain in memory/swap)\n- ⚠️ No hardware security module or secure enclave protection\n- ⚠️ **Not recommended for production or highly sensitive credentials**\n\n**Key management:**\n\n- Key file: `~/.config/keyring/file.key` (auto-generated if missing)\n- Override with `KEYRING_FILE_MASTER_KEY` env var (64 hex chars = 32 bytes)\n- Changing systems requires copying/regenerating key file\n\n**Acceptable use cases:**\n\n- Development and testing environments\n- CI/CD pipelines where native backends are unavailable\n- Non-sensitive credential storage\n- Environments where you control key distribution\n\n### CLI Security Best Practices\n\n**Secure password input:**\n\n- ✅ Use `cross-keychain set service username` (interactive prompt - secure)\n- ✅ Use `cross-keychain set service username --password-stdin < file` (reads from stdin)\n- ⚠️ Avoid `echo \"password\" | cross-keychain set service username` (may appear in process lists)\n\n**Production recommendations:**\n\n- **Always use native OS backends** (keychain, windows, secret-service)\n- Never use file backend for production secrets\n- Use environment variables or CI/CD secret management for automation\n- Avoid command line password arguments\n\n### Data Storage Locations\n\nCredentials and configuration files are stored in platform-specific directories:\n\n**Windows:**\n\n- `%LOCALAPPDATA%\\Keyring` or `%APPDATA%\\Keyring`\n\n**macOS:**\n\n- Data: `~/.local/share/keyring` (or `$XDG_DATA_HOME/keyring`)\n- Config: `~/.config/keyring` (or `$XDG_CONFIG_HOME/keyring`)\n\n**Linux:**\n\n- Data: `~/.local/share/keyring` (or `$XDG_DATA_HOME/keyring`)\n- Config: `~/.config/keyring` (or `$XDG_CONFIG_HOME/keyring`)\n\n## Testing & Development\n\nThis project uses modern development tools and practices. Here are the key npm scripts for contributors:\n\n### Core Development Scripts\n\n- **`npm run test`** – Run the complete Vitest test suite with coverage reporting\n- **`npm run test:watch`** – Run tests in watch mode for active development\n- **`npm run lint`** – Lint source code with ESLint to enforce code quality standards\n- **`npm run lint:fix`** – Automatically fix linting issues where possible\n- **`npm run build`** – Build the project using tsup (TypeScript bundler)\n- **`npm run typecheck`** – Run TypeScript compiler for type checking without emitting files\n\n### Additional Utility Scripts\n\n- **`npm run coverage`** – Generate detailed test coverage reports\n- **`npm run format`** – Format code using Prettier\n- **`npm run format:check`** – Check code formatting without making changes\n- **`npm run ci`** – Run the complete CI pipeline locally (lint + typecheck + test + build)\n- **`npm run security`** – Run npm audit to check for security vulnerabilities\n- **`npm run deps:check`** – Check for outdated dependencies\n- **`npm run deps:unused`** – Find unused dependencies with knip\n\n### Getting Started for Contributors\n\n1. **Clone and install dependencies:**\n\n   ```sh\n   git clone https://github.com/magarcia/cross-keychain.git\n   cd cross-keychain\n   npm install\n   ```\n\n2. **Run the development workflow:**\n\n   ```sh\n   npm run test:watch  # Start tests in watch mode\n   npm run lint        # Check code quality\n   npm run typecheck   # Verify TypeScript types\n   ```\n\n3. **Before committing:**\n   ```sh\n   npm run ci  # Run full CI pipeline locally\n   ```\n\nThe project uses Husky for Git hooks to automatically run linting and tests before commits.\n\n## Contributing\n\nContributions and bug reports are welcome! Read the [CONTRIBUTING.md](CONTRIBUTING.md) guide and adhere to the [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) when participating. Issues and pull requests live at the [GitHub repository](https://github.com/magarcia/cross-keychain).\n\n## License\n\nReleased under the [MIT License](LICENSE).\n","readmeFilename":"README.md"}