Skip to content

Latest commit

Β 

History

51 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Summon πŸŒ€

Everything you reuse, one keystroke away.
FastΒ Β Β·Β Β  PrivateΒ Β Β·Β Β  On-deviceΒ Β Β·Β Β  Keyboard-first

CI License: AGPL-3.0 Platform: macOS 26+ Swift 6.1 SwiftData Dependencies: none

summon.technology β†’ or build from source

The Summon panel, searching for invoice


Why Summon?

Narrower on purpose. Summon is not a notes app β€” it isn't where you write. It's not a file manager β€” it isn't where you keep everything. It holds only the handful of things you reach for again and again: the canned reply, the portfolio PDF, the VAT number, the headshot. Because it holds less, it can be much better at the one moment that matters β€” getting one of them to where your cursor already is.

One keystroke, wherever you are. Press βŒ₯Space in any app, type a few characters, press ↩, and the thing lands in the app you were already using. No window to find, no tab to switch to, no clipboard to babysit. The panel appears over your work and disappears again.

Private because it never leaves. There is no account, no sync, no telemetry, and no networking code β€” the app makes no outbound requests at all. Anything you mark sensitive is encrypted with AES-GCM under a key wrapped by your PIN β€” or by a passphrase, if you want something that holds up against someone who has your disk β€” so a locked item stays findable by name while revealing nothing of its contents, its file, or even its OCR'd text.

Fast, and measured rather than claimed. A keystroke re-ranks 2,000 items in 0.55 ms. Those numbers are asserted by the test suite against budgets that fail the build β€” because the first time performance was "fixed" here, the benchmark and the app disagreed about what was being measured, and nobody noticed for a whole commit.


Summon in 30 seconds

⌨️ Summon anything

βŒ₯Space from any app, type a few letters, ↩. It pastes into whatever you were just using. Fuzzy search, ranked by what you actually reach for.

πŸ“Ž Keep what you reuse

Text, rich text, images, PDFs, whole files. Dropped in, copied in, or grabbed from your selection without leaving the app you're in.

πŸ“ Fill in the blanks

Hi {{first_name}}, turns the panel into a small form. Dates, the clipboard, and where the caret lands are placeholders too.

πŸ—‚ Organise lightly

Nested folders you drag to rearrange, each with its own icon and colour. Plus tags, pins, and a search that reaches inside PDFs.

πŸ”’ Lock what's private

Mark anything sensitive and it's encrypted at rest. Titles stay searchable; contents don't. Unlock with a PIN or a passphrase, auto-lock on a timer.

🏠 Never leaves the Mac

No account, no sync, no analytics, no networking code. Your library is a folder you can back up yourself.


Feature tour

⌨️ The summon moment

The panel before typing, showing pinned and recent items

Press βŒ₯Space and the panel appears over whatever you're doing without disturbing it. Before you type anything it shows what you're most likely to want: things you've pinned, then things you've used recently. If you've used an item in this app before, a section named for that app comes first β€” so Mail surfaces your canned replies and Finder surfaces your documents.

Type, and results are ranked by a fuzzy match, by how often and how recently you've used each item, and by whether you've used it in this app before. The characters that matched are picked out in the title, so it's always clear why a result is in the list.

↩ pastes it where your cursor already was. βŒ˜β†© copies instead, βŒ₯↩ opens the file, ⇧↩ pastes as plain text, and ⌘1β€“βŒ˜9 jump straight to a result.

πŸ“Ž Getting things in

Four ways, because the friction of saving is what decides whether a tool like this gets used at all:

  • Drag and drop files onto the panel, the library, or a specific folder.
  • βŒ₯⇧S saves whatever is selected right now, without leaving the app you're in. In Finder it reads the selection directly; elsewhere it copies, reads the pasteboard, and puts your previous clipboard contents back.
  • Clipboard history keeps the last 40 things you copied so you can promote one into the library. It skips anything a password manager marks as concealed, and ignores 1Password, Bitwarden and Keychain Access entirely.
  • "Add to Summon" registers in the system Services menu, so it appears in the right-click menu of every app.

πŸ“ Fill-in fields

A snippet can have blanks:

Hi {{first_name}},

Thanks for getting in touch about {{topic:your enquiry}}. I will come back to you
by {{date:+3d}}.

{{cursor}}

Best,
Hein

Choose it and the panel becomes a small form β€” Tab between fields, ↩ to insert. Repeating a name reuses the value you already typed. {{date}}, {{time}} and {{clipboard}} fill themselves in, and the caret ends up exactly where {{cursor}} was.

πŸ—‚ Folders, tags and pins

The Summon library window

Folders nest as deep as you like and rearrange by dragging β€” drop onto a folder to nest inside it, between two to reorder. Each gets its own icon and colour from a curated set of about ninety SF Symbols, searchable by meaning rather than by name: "money" finds the currency symbols, "vat" finds percent, "password" finds the key.

The folder icon picker

Tags cut across folders, pins float things to the top, and filters can be typed inline: #tag, /folder, img:, pdf:, txt:, file:, pinned:, locked:.

⌘K β€” every action, without the clutter

The Command-K action menu open over the panel

⌘K opens every action for the selected item, searchable, with each shortcut beside it: paste, paste as plain text, copy, open, reveal in Finder, rename, move to folder, add tag, pin, mark sensitive, delete.

It exists in the panel and only in the panel. A window has room to show its actions, so the library shows them rather than hiding them a level down.

πŸ”’ Sensitive items

Mark an item or a whole folder sensitive and its contents are encrypted at rest:

  • A random 256-bit master key is generated once. Per-item keys derive from it via HKDF using the item's own UUID, so no key is ever reused across two items.
  • That master key is wrapped twice β€” under your PIN or passphrase with PBKDF2-SHA256 at 600,000 iterations, and separately in the Keychain behind Touch ID. Changing it re-wraps one small key, so it is instant rather than a re-encryption of everything. Switching between a PIN and a passphrase is that same re-wrap: nothing is decrypted.
  • Unlocked, the key exists only in memory. It is discarded on lock, on a timeout you choose, and on sleep. Decrypted scratch copies go at the same moment.
  • Five wrong guesses start an escalating cooldown that survives a relaunch, on every path that takes a guess β€” unlocking and changing it both.

A PIN and a passphrase defend against different people. Four digits is 10,000 combinations, and the cooldown that makes that reasonable only applies to someone typing into this app. It cannot apply to someone who has copied the library folder, because vault.wrap is just a file and they can guess against it offline as fast as their hardware allows β€” at which point 10,000 is not a wait. A PIN is the right default for the summon moment and enough to stop someone who wanders past an unlocked Mac. Choose a passphrase if the threat you have in mind is someone walking off with the disk.

Titles stay visible; contents do not. A locked item is still findable by name and tag, but matches nothing in its body, its file, or its OCR'd text β€” that last one is what stops a locked passport scan being found by searching its own contents. Sensitive content is never handed to the language model either, local though it is.

✨ On-device intelligence, never load-bearing

Images are OCR'd with Vision and PDFs are read with PDFKit, so their contents are searchable β€” that is how "discovery call" finds a phrase buried on page four of a PDF. Apple's on-device model suggests a title, tags and a one-line summary when you import something, and rewrites a snippet in a different register.

If Apple Intelligence is off, unavailable, or still downloading, every one of those falls back to plain heuristics and the app carries on. Settings shows the real status rather than pretending.

πŸŒ— Light and dark

The Summon panel in light mode

Both appearances are tuned separately, and every text tier is asserted by a test to clear WCAG AA contrast against its background β€” because the first monochrome palette looked perfectly fine on screen while sitting at 2.65:1.


⌨️ The keyboard model

Every binding lives in one place, PanelKeyMap, and every one is asserted by a test. The panel once drew ⌘1β€“βŒ˜9 on each row with no handler behind them, which is precisely what that arrangement exists to prevent.

Global β€” anywhere in macOS

Key Does
βŒ₯Space Summon the panel
βŒ₯⇧S Save the current selection without leaving the app you are in

In the panel

Key Does
↩ Paste into the app you were just in
βŒ˜β†© Copy instead
βŒ₯↩ Open the file
⇧↩ Paste as plain text
⌘1β€“βŒ˜9 Jump straight to a result
↑ ↓ Move β€” β‡ž β‡Ÿ by eight, βŒ˜β†‘ βŒ˜β†“ or β†– β†˜ to the ends
⌘K Actions for the selected item
⌘P ⌘R ⌘⌫ Pin · reveal in Finder · delete, with a confirmation
β‡₯ Narrow to the folder the selected item lives in
⌫ Leave that folder again, when nothing is typed
βŽ‹ Back exactly one level: menu β†’ mode β†’ query β†’ folder β†’ closed

Hold ⌘, βŒ₯ or ⇧ and the footer relabels itself to say what ↩ will do right now, so the shortcuts are learned by using them rather than by reading this table.

In the library window

Key Does
⌘L ⌘, ⌘Q Open the library · Settings · Quit
⌘N β‡§βŒ˜N ⌘O New snippet Β· new folder Β· import files
⌘P ⌘⌫ β‡§βŒ˜L Pin Β· delete Β· lock or unlock sensitive items
↑ ↓ ← β†’ Move through the list or the grid

The panel deliberately does not claim ⌘C, ⌘V, ⌘A or ⌘Z β€” those belong to the search field, and the tests assert the fall-through as well as the bindings, because a key map that grabs everything breaks text editing in a way no positive test would catch. One casualty: ⌘⌫ means Delete here, so the field loses delete-to-start-of-line.


πŸ›  Build it

git clone https://github.com/heindewilde/summon
cd summon
Scripts/create-signing-identity.sh   # once β€” see Permissions, below
Scripts/build-app.sh                 # release build β†’ dist/Summon.app
open dist/Summon.app

Requires macOS 26 and Xcode 26 (Swift 6.1+). There are no dependencies β€” the entire app builds from the system SDK.

Scripts/run.sh              # debug build, then launch
Scripts/run.sh --demo       # …against a throwaway library
Scripts/selftest.sh         # 90 runtime checks on a fresh demo library
swift test                  # 248 tests over the logic layer
swift test -c release       # the same, plus the performance budgets

SUMMON_DEMO=1 points the app at a separate library (Summon-Demo), so you can experiment without touching your real one.

⬇️ Downloads

Coming soon. A notarised build will land at summon.technology once Summon has an Apple Developer ID. Until then, building takes two minutes and the three commands above.


πŸ” Permissions

Summon asks for as little as it can, and every request has a fallback.

Permission What for If you say no
Accessibility Pressing ⌘V for you after you choose something Summon copies instead and shows "press ⌘V". One extra keystroke, nothing else changes
Apple Events (Finder) Reading your Finder selection for βŒ₯⇧S Summon copies the selection and restores your clipboard afterwards

Global shortcuts need no permission at all β€” they use Carbon's RegisterEventHotKey rather than an event tap, specifically so βŒ₯Space works the moment you launch the app.

Why the signing script matters

Scripts/create-signing-identity.sh creates a self-signed certificate in your login keychain, and the build script uses it when present. This is not ceremony. macOS binds permissions like Accessibility to an app's code signature, and an ad-hoc signature is just a hash of the binary β€” so every rebuild looks like a different app and silently loses the grant you gave it. With a stable certificate you grant it once.

The certificate is local only. No other Mac trusts it, and deleting it from Keychain Access reverts the build to ad-hoc signing with a warning.


πŸ—„ Where your data lives

~/Library/Application Support/Summon/
β”œβ”€β”€ Library.store     SwiftData β€” titles, tags, folders, plaintext bodies
β”œβ”€β”€ Blobs/            managed copies of imported files
β”œβ”€β”€ Vault/            AES-GCM sealed blobs
β”œβ”€β”€ Thumbnails/
└── vault.wrap        the wrapped master key, its salt, kind, and lockout state

Files you add are copied into Blobs/ rather than referenced, so moving or deleting the original never breaks an item β€” and content hashing means adding the same file twice dedupes instead of piling up.

Backing up is copying that folder. It is self-contained, and sensitive items stay encrypted inside it. Time Machine already covers it.


⚠️ Known limits

Stated plainly, because an app that asks for Accessibility and holds your bank details ought to be the kind that tells you what does not work.

Auto-paste needs Accessibility. It is used for exactly one thing: pressing ⌘V for you. Decline it and everything else still works.

Launch it with open, not by running the binary directly. macOS attributes a permission to the responsible process, so a binary exec'd from a terminal inherits the terminal and reports Accessibility as denied even when the grant is in place.

Touch ID unlock is unavailable in a locally-signed build. A key guarded by SecAccessControl lives in the data-protection keychain, which requires a keychain-access-groups entitlement prefixed with an Apple Team ID. A local build has no team, and adding the entitlement unprefixed makes the app fail to launch outright. Summon probes for this at runtime and hides the option rather than offering something that throws. PIN and passphrase unlock are unaffected β€” the master key is wrapped twice, independently, for exactly this reason.

Apple Intelligence is optional. Every feature that uses it falls back to deterministic heuristics when it is off, ineligible, or still downloading.

No sync, and no iOS app yet. Deliberate. The schema has followed CloudKit's rules since the first commit; the sync code does not exist.

OCR covers English and Dutch. Hard-coded, and easy to extend.

Summon is unsandboxed, which it must be to read a Finder selection over Apple Events and to paste into other apps.


πŸ— Architecture at a glance

Sources/
  SummonKit/       Pure logic. No views. The entire test surface.
    Model/         SwiftData models, LibraryStore, folder icons, starter library
    Vault/         AES-GCM sealing, PIN/passphrase wrapping, Touch ID, lock lifecycle
    Storage/       Managed blob store, content hashing, scratch materialisation
    Search/        Fuzzy scorer, frecency, app affinity, query parser, index cache
    Keyboard/      PanelKeyMap β€” every binding, as pure data
    Snippets/      Placeholder parsing and rendering
    Intelligence/  Heuristics, Vision and PDF extraction, on-device model
    Capture/       Clipboard monitor, selection capture, importer
    Insertion/     Pasteboard writing, focus restore, synthetic paste
    HotKeys/       Carbon RegisterEventHotKey wrapper
    Support/       Library paths, logging, RTF
  SummonUI/        SwiftUI views, design tokens, AppModel
  Summon/          Executable: scenes, panel window, menu bar, test harnesses

SwiftData models are main-actor bound and not Sendable, so ranking works over ItemSnapshot value types instead. That keeps concurrency simple and makes the whole search layer testable without a store.

The schema follows CloudKit's rules from day one β€” no unique constraints, every relationship optional with an inverse, every attribute defaulted β€” so an iOS companion is additive rather than a migration. No sync code ships today.

The panel and the library draw the same row. Three surfaces had grown three heights and three type scales before they were collapsed into one component; density cannot drift between them now.


🧰 Tech stack

Language Swift 6.1, strict concurrency
UI SwiftUI, with AppKit where SwiftUI cannot reach β€” a non-activating NSPanel, an NSTextField search field so the arrow keys can drive the list, NSTextView for rich text
Data SwiftData over SQLite
Crypto CryptoKit (AES-GCM, HKDF), CommonCrypto (PBKDF2), Security, LocalAuthentication
Intelligence Vision, PDFKit, FoundationModels
Shortcuts Carbon RegisterEventHotKey β€” no permission required
Dependencies None

πŸ§ͺ How it is verified

248 tests across 37 suites The whole logic layer: vault round-trips and wrong-secret rejection, the cooldown holding against a clock set backwards, that extraction opens no socket and that a seal leaves no plaintext in the store file, ranking and frecency, placeholder parsing, folder trees and cycle refusal, every keyboard binding and the keys the panel deliberately declines, contrast ratios, and content edge cases from empty titles to right-to-left text
90 runtime checks Scripts/selftest.sh drives the real app: hot key registration, panel window configuration, search reaching inside a PDF, the vault lifecycle end to end, and each keyboard binding actually reaching behaviour
Performance budgets Asserted in release builds, and they fail the build. The wall-clock ones run locally; CI keeps the structural assertions, because timing on a shared runner measures the runner
A paste round trip Opens a scratch document in TextEdit, summons a snippet into it, and reads the result back through the Accessibility API β€” refusing to run unless TextEdit is genuinely frontmost
Budget Measured Limit
Keystroke β†’ results, 2,000 items 0.55 ms 4 ms
Empty query β€” runs on every panel open 0.31 ms 4 ms
Index build, 2,000 items 13.5 ms 25 ms
Typing rebuilds the index never asserted structurally

Budgets assert on the fastest of many runs, not the mean or the worst. Timing noise is one-sided β€” the scheduler only ever adds time β€” so the minimum is the honest estimator of what the code can do, and asserting the maximum produces a flaky suite whose usual fix is raising the budget until it means nothing.


πŸ—Ί Roadmap

  • A notarised release β€” signed downloads, with Touch ID unlock arriving alongside them
  • iCloud sync β€” the schema has followed CloudKit's rules since the first commit, so this is additive rather than a migration
  • An iOS companion β€” for the same reason
  • Keyword expansion β€” type ;sig anywhere and have it expand in place
  • Smart collections β€” saved searches that behave like folders

🀝 Contributing

Issues and pull requests are welcome.

swift test                  # start here β€” it is fast
swift test -c release       # includes the performance budgets
Scripts/selftest.sh         # drives the real app end to end

Two things worth knowing before digging in:

  • SummonKit has no UI imports. If logic can live there, it should β€” that is what keeps it testable without launching an app.
  • Test harnesses never reach outside the app. AppModel.isHarness makes delivery record what would have been pasted rather than writing your clipboard or synthesising a ⌘V. This is not theoretical: the self-test once pasted into someone's email, repeatedly, and the guard now defaults on for any SUMMON_* variable so a new harness cannot forget.

❓ FAQ

Does anything leave my Mac? No. There is no networking code in the app at all β€” no account, no sync, no analytics, no crash reporting.

Do I have to grant Accessibility? No. Without it Summon copies and tells you to press ⌘V. The global shortcut works either way.

What happens if I forget my PIN or passphrase? The encrypted contents are unrecoverable β€” that is what encryption means. Titles, tags and folders stay readable, so you will still see what you have lost.

Which should I pick? A PIN unless you have a reason not to: it is four boxes that fill themselves, which is what makes unlocking feel like no step at all. Switch to a passphrase in Settings β†’ Lock if you are protecting something that would matter to a person holding your disk rather than to a person passing your desk. Switching either way is instant and decrypts nothing.

Do I need Apple Intelligence? No. It improves suggested titles and tags when available and falls back to plain rules when it is not.

Why is it unsandboxed? To read a Finder selection over Apple Events and to paste into other apps. A sandboxed build could do neither.

Can I use a different shortcut? Yes, both are rebindable in Settings. If another app already owns your choice, Summon tells you rather than failing quietly.

Why AGPL for a desktop app? Consistency with the rest of this profile. Its distinguishing network clause has no practical effect on an app that never serves anyone over a network.


πŸ™ Acknowledgements

Shaped by the tools that got the shape right first β€” Raycast for what a launcher should feel like, Alfred for proving the idea, and Maccy for showing how small a clipboard manager can be.


πŸ“œ License

AGPL-3.0 Β© Hein de Wilde

About

Summon is the fast and private Mac library for the text, images and files you keep reusing πŸŒ€

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages