Key-value notes in your menu bar.
One shortcut to open. Keyboard-first. Masked values for secrets.
- π Key-value notes stored locally in a plain JSON file β no cloud, no accounts
- β‘ Global shortcut (
ββ§K/Ctrl+Shift+K) β open from anywhere - β¨οΈ Keyboard-first navigation β arrow-keys to move, Enter to copy, Escape to hide
- π Value masking for secrets β toggle on/off per session
- π Themes β system / light / dark
- π Configurable data file location β keep your notes anywhere
- πΉ Customizable shortcuts β global toggle, new note, focus search
- π Secure-by-default Electron β
contextIsolation: true,nodeIntegration: false
- Node.js 24.15.0 β pinned via
.nvmrc(read by nvm/fnm/asdf/volta) and enforced softly byengines.nodeinpackage.json - Electron 40 β desktop shell with secure IPC (
contextIsolation: true,nodeIntegration: false) - TypeScript β across main, preload, and renderer
- electron-vite β dev server with HMR and production build
- Vitest β unit tests with V8 coverage (100% threshold on main process)
- Playwright β E2E tests driving a packaged Electron build
- electron-builder β packaging and installers
- ESLint + Prettier β linting and formatting
- JSON file storage β no native dependencies; notes persisted to a plain JSON file
brew install --cask moreiraeugenio/keycache/keycacheThe first run shows a Gatekeeper warning (the app is unsigned β see Unsigned caveat).
Grab the latest installer for your platform from Releases:
- macOS β
Keycache-<version>-arm64.dmg(Apple Silicon) /Keycache-<version>-x64.dmg(Intel) - Windows β
Keycache.Setup.<version>.exe - Linux β
Keycache-<version>.AppImage
nvm use # or `fnm use` β activates the Node version pinned in .nvmrc
npm install
npm run devThe window starts hidden β look for the Keycache icon in the menu bar / system tray, or press the default global shortcut ββ§K (macOS) / Ctrl+Shift+K (Windows, Linux) to toggle it.
| Command | Description |
|---|---|
npm run dev |
Launch dev server with HMR |
npm run start |
Alias for dev |
npm run build |
Production build to out/ |
npm run preview |
Preview the production build (electron-vite preview) |
npm run test |
Run unit tests |
npm run test:watch |
Run unit tests in watch mode |
npm run test:coverage |
Run unit tests with coverage report |
npm run test:e2e |
Build + run Playwright E2E tests |
npm run lint |
Lint with ESLint |
npm run format |
Format with Prettier |
npm run package |
Build + package unpacked app to dist/ |
npm run package:dev |
Like package, but as "Keycache Dev" with appId com.keycache.dev (side-by-side with a prod install) |
npm run dist |
Build + create distributable installer to dist/ |
Releases are built and published via GitHub Actions (.github/workflows/release.yml). The workflow produces unsigned artifacts for macOS (.dmg + .zip), Windows (.exe), and Linux (.AppImage).
Nothing runs locally. A release is two steps, both on GitHub:
1. Open the release PR. Run the Open release PR workflow (.github/workflows/release-pr.yml) from the Actions tab. release-please opens a chore(main): release X.Y.Z pull request carrying the package.json version bump and the CHANGELOG.md entry for every commit since the last tag. An optional release-as input forces a specific version instead of deriving one.
2. Merge it. Merging that PR is the release β until then nothing is tagged and nothing is published.
The merge triggers release.yml, which runs end to end in a single Actions run:
- release-please tags the merge commit (
vX.Y.Z) and creates the GitHub Release as a draft macos-latest/windows-latest/ubuntu-latestbuild their native artifacts in parallel- The artifacts are attached to the release, which is then published
- A cask-bump PR is opened against the Homebrew tap
The release is drafted rather than published up front so it is never visible without its binaries β the tag exists for the few minutes the builds take.
Because the release PR is an ordinary pull request, ci.yml runs against it first: lint, unit tests with coverage, build, and E2E on all three platforms. A release can only be cut from a tree that has passed all of them.
Commits that land after the PR is opened are not picked up. The version and changelog freeze at dispatch time. If more work merges before you release, re-run Open release PR β it refreshes the existing PR in place.
The bump level comes from Conventional Commit prefixes since the last tag, mapped to semver:
- patch β bugfix, no behavior change (
fix:,chore:,docs:,refactor:,test:,ci:,build:,perf:,style:,revert:) - minor β new feature, backwards-compatible (
feat:) - major β breaking change (any type with
!likefeat!:/fix!:, or aBREAKING CHANGE:footer)
The highest-severity match across all commits since the last tag wins. To see the level release-please picked, read the title of the open release PR β it is recomputed on every push to main.
If you'd rather run the steps by hand (to pick a different bump level, skip npm ci, etc.):
git switch main
git pull
nvm use # or `fnm use`
npm ci
npm run lint && npm run test && npm run build
npm version <patch|minor|major> -m "chore: release v%s"
git push origin main
git push --tagsUse the Actions tab's Run workflow button to trigger the matrix without publishing β artifacts are uploaded to the workflow run for inspection only.
- macOS users see a Gatekeeper warning on first launch; right-click the app β Open to bypass.
- Windows users see a SmartScreen warning; click More info β Run anyway.
Code signing (Apple Developer cert, Windows EV cert) is not set up; enabling it requires the certs plus additional GitHub secrets.
After each tagged release, .github/workflows/release.yml opens a PR against moreiraeugenio/homebrew-keycache bumping Casks/keycache.rb to the new version + sha256 (per-arch). Merging that PR ships the update to brew upgrade users.
Requires the HOMEBREW_TAP_TOKEN repo secret β a PAT (classic) with repo scope on the tap repo, or a fine-grained token scoped to moreiraeugenio/homebrew-keycache with Contents: write + Pull requests: write.
keycache.app lives in site/ β plain HTML, CSS and one script, no framework and no build step. .github/workflows/deploy-site.yml deploys it to Vercel on every push to main that touches site/.
npm run site:assets # copy demo.gif in from assets/ (gitignored under site/)
npx serve site # preview locally (or: cd site && python3 -m http.server 8000)
npm run site:images # re-render the OG card and favicon after editing themSee site/README.md for the Vercel project setup and the required secrets.
A properly formed Git commit subject line should always be able to complete the following sentence:
If applied, this commit will your subject line here
[type](optional scope): [subject]
Must be one of the following:
- build - build related changes
- ci - CI related changes
- chore - build process or auxiliary tool changes
- docs - documentation only changes
- feat - a new feature
- fix - a bug fix
- perf - a code change that improves performance
- refactor - a code change that neither fixes a bug nor adds a feature
- revert - reverting things
- style - markup, white-space, formatting, missing semicolons, etc
- test - adding missing tests
The subject contains a succinct description of the change:
- Use the imperative, present tense: "change" not "changed" nor "changes"
- No dot (.) at the end
Just as in the subject, use the imperative, present tense: "change", not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
The 7 rules of a great commit message:
- Separate subject from body with a blank line
- Limit the subject line to 50 characters
- Summary in present tense. Not capitalized
- Do not end the subject line with a period
- Use the imperative mood in the subject line
- Wrap the body at 72 characters
- Use the body to explain what and why vs. how
Copyright (c) 2026 EugΓͺnio Moreira
Licensed under the GNU General Public License v3.0 or later. See LICENSE for the full text.
