How to Verify Comoji Never Sends Your Keystrokes Anywhere
Comoji asks for Accessibility and Input Monitoring, the two most invasive permissions macOS has. We say everything is processed locally and that nothing you type is ever uploaded. Someone on Reddit put the obvious objection well: they mostly believed us, but it still felt strange to hand over those permissions on nothing but our word.
That is a fair objection, and the answer should not be “trust us harder.” This post is the HOWTO: every command below is read-only, takes about five minutes, and lets you confirm on your own machine what Comoji does and does not do on the network. The reference version of all of it, including the complete list of hosts, lives on the security page.
What you are actually checking
A keylogger has to do something with what it captures. It sends it somewhere, or it writes it somewhere. That is a small, concrete claim to test, and macOS ships the tools to test it: you can list every network socket a process has open, count every byte it has sent, read the hostnames baked into the binary it shipped as, and block its network access entirely and watch what breaks.
Nothing here is Comoji-specific. Use exactly the same steps on any Mac app that asks for these permissions, including ours. That is the point.
Before you start
Have Comoji running and open Terminal (Applications → Utilities → Terminal). Every command below is a read-only inspection: nothing installs, changes, or deletes anything. If you installed a development build, the process is named ComojiDev and you should substitute that everywhere Comoji appears.
First, confirm it is running and get its process ID:
pgrep -x ComojiYou should get back a number. If you get nothing, Comoji is not running (it is a menu bar app, so check the menu bar rather than the Dock).
Step 1: watch its network connections while you type
lsof lists open files, and on a Unix system a network socket is a file. Restricted to Comoji’s process ID, it shows every connection the app has open right now:
lsof -nP -i -a -p "$(pgrep -x Comoji)"The expected result is no output at all. An empty response means the process holds no network connections. Now go type: open Messages, fire off a few :fire, :tada, and :liz completions, write a paragraph or two, then run the command again. Still empty.
A single snapshot could in principle miss a connection that opens and closes between checks, so watch it continuously instead. Paste this into a spare Terminal window and leave it running while you use your Mac normally:
while :; do
printf '%s ' "$(date +%T)"
lsof -nP -i -a -p "$(pgrep -x Comoji)" | tail -n +2 | awk '{print $9}' | paste -sd' ' -
echo
sleep 2
doneYou get a timestamp every two seconds with nothing after it, no matter how much you type. Press Control-C to stop.
Then, while that loop is still running, do something that is *supposed* to use the network: type /gif in Messages and search for something. Now you will see api.giphy.com and a giphy.com CDN host appear next to the timestamps. That contrast is the useful part of this test. It proves the loop would have shown you a connection if there had been one, so the empty lines while you were typing mean what they appear to mean.
Step 2: count the bytes
nettop tracks cumulative traffic per process, so it catches anything too brief for a sampled check:
nettop -P -p "$(pgrep -x Comoji)"Leave it open, type for a few minutes, and keep away from the /gif and /sticker pickers. The bytes_in and bytes_out columns stay at zero. Press q to quit.
Step 3: read the hostnames out of the app you downloaded
A process can only connect somewhere it knows about, and hostnames have to be stored in the binary. strings pulls readable text out of a compiled file, so you can list them without any source code:
strings -a /Applications/Comoji.app/Contents/MacOS/Comoji \
| grep -Eo 'https?://[A-Za-z0-9._-]+' | sort -u
/usr/libexec/PlistBuddy -c 'Print :SUFeedURL' \
/Applications/Comoji.app/Contents/Info.plistWhat comes back is Google’s sticker artwork CDN (fonts.gstatic.com), the update feed in Firebase Storage (storage.googleapis.com), and a handful of comoji.io and other-project addresses that only ever get handed to your browser when you click a link in the app.
Two honest caveats, so this matches what you actually see rather than a tidied-up version. api.giphy.com is assembled from its hostname in code rather than stored as a full URL, so it will not show up in that first list; search for it directly with strings -a … | grep -Eo '[a-z0-9.-]*giphy\.com', a pattern that matches hostnames only so it will not print the API key that also ships in the bundle. And the bundled Sparkle update framework carries its own documentation URLs (sparkle-project.org, andymatuschak.org) inside Contents/Frameworks, which are strings in a third-party library rather than hosts Comoji contacts.
Step 4: block it and see what breaks
This is the strongest test, because it does not depend on trusting any output. Install an outbound firewall: LuLu is free and open source, and Little Snitch is the long-standing commercial option. Set it to alert on every outbound connection, then deny everything from Comoji, permanently.
Emoji autocomplete keeps working exactly as it did, indefinitely, because it never needed the network in the first place. The only things that stop working are the update check and downloading sticker or GIF artwork you have not already cached. If the local claim were false, this is where it would fall apart, loudly.
Step 5: confirm the app is genuinely ours
All of the above tests the copy on your disk. It is worth confirming that copy is the one Apple notarized and that nobody has modified it since:
codesign -dv --verbose=4 /Applications/Comoji.app
spctl -a -vvv -t exec /Applications/Comoji.app
codesign -d --entitlements - /Applications/Comoji.appcodesign prints the Developer ID the app is signed with, spctl should answer accepted with source=Notarized Developer ID, and the entitlements dump shows what the app is permitted to do at all.
What we do on our side
Verification you can perform is worth more than a promise, but the promise should also be enforced somewhere. Two things run on every build of the app:
- The complete list of hosts Comoji may contact is a declaration in the source, published as the network surface and shown in the app under Preferences → Privacy. An automated test fails the build if the app can reach a host that list does not disclose.
- A second test scans the code that actually handles keystrokes, the event tap, the token buffer, the matcher, and the inserter, and fails the build if any networking API or any remote URL appears in it. The part of Comoji that sees what you type contains no networking whatsoever, and cannot quietly acquire any.
That same page also records what we have decided *not* to do and why, including open-sourcing the input path and commissioning a paid third-party review, so you can weigh the reasoning instead of guessing at it.
If you run any of this and find something that contradicts what we publish, mail hello@comoji.io. We would rather correct the page or the app than have the claim quietly be wrong.
For what the app actually does with those permissions, the features page covers every app and site Comoji works in and the per-app and per-website disable lists that keep it quiet where you would rather it not run at all.
Frequently asked questions
How can I tell if a Mac app is sending my keystrokes somewhere?
Run lsof -nP -i -a -p "$(pgrep -x AppName)" in Terminal to list the network connections the app currently has open, and nettop -P -p "$(pgrep -x AppName)" to see cumulative bytes sent and received. For a decisive test, install an outbound firewall such as LuLu or Little Snitch, block the app entirely, and check whether its features still work.
Does Comoji upload what I type?
No. Comoji has no account, no telemetry, no analytics, and no crash reporter. Keystrokes are held in a short-lived in-memory buffer only to detect a colon-prefixed shortcut, and are never written to disk or transmitted. The app contacts exactly four hosts: the update feed, Google’s sticker artwork CDN, and the GIPHY API and CDN when you use the /gif picker.
Why does Comoji need Accessibility and Input Monitoring?
Input Monitoring lets it notice when you type a shortcut like :fire. Accessibility lets it find the focused text field and the caret position so the popup appears in the right place, and type the emoji back into that field. Neither is used to read the contents of your text fields, and secure password fields are ignored entirely.
Is Comoji open source?
Not currently. The security page on comoji.io records that decision along with the reasoning, publishes the complete network surface, and gives step-by-step commands to verify the local-processing claim against the notarized binary you downloaded.
Try Comoji
Slack- and Discord-style :emoji: autocomplete, everywhere on your Mac. Free.