Getting started
Download or build nooklet, run a server, open the web, desktop, iOS and experimental Android apps, and pair devices with tokens or a pairing link.
Pick a setup#
| Setup | Use it when | Devices |
|---|---|---|
| Recommended: a server on your tailnet | You want more than one device. | Every device on your Tailscale network, over HTTPS. |
| One machine | You want to try nooklet, or you only use one computer. | The machine itself. |
| Public server behind a TLS proxy | You cannot use Tailscale. | Anything; read the checklist in Self-hosting first. |
Plain http:// to another machine does not work: browsers only grant the APIs the client needs to
https:// pages and localhost. See Security for why the tailnet setup is the
default.
Download a release#
Each tagged version is on the Releases page: the
desktop app for macOS, Linux and Windows, an experimental Android APK, release notes, and a
SHA256SUMS file. The server is a container image, ghcr.io/hnykda/nooklet:<version> (for
example 0.1.0, or latest); see Docker. iOS has no download: build it
from source (below).
Not every build gets the same testing. The macOS app and the server are used daily; the Linux and Windows apps are built in CI and not yet tested by the maintainer; Android is experimental and has never been run by the maintainer. The README's platform table has the details.
To check a download: shasum -a 256 -c SHA256SUMS --ignore-missing in the folder holding both
(sha256sum -c on Linux, Get-FileHash on Windows).
Install from source#
You need Node 24 or newer, pnpm 12, and git.
git clone https://github.com/hnykda/nooklet.git
cd nooklet
pnpm install
pnpm --filter @nooklet/web build # the web client the server serves
pnpm nooklet <command> runs the CLI from source. It runs with packages/server as its working
directory, so pass absolute paths to import, --data and similar flags.
pnpm nooklet --help lists every command.
Building from source covers every build target, the tests and the toolchain.
One machine#
pnpm nooklet serve
Open http://127.0.0.1:6100. The server redirects to /g/default/, the default graph, and the app
opens on today's journal. A browser on the same machine gets a token automatically, so there is no
login.
The data lives in ~/.nooklet/default unless you pass --data <dir> or set NOOKLET_DATA. The
graph is graphs/default/graph.sqlite inside it, with the markdown mirror next to it in pages/
and journals/. On first start the server prints a root token; save it. You need it to create
graphs or list them from another device. pnpm nooklet token root prints it again.
Bring a Logseq graph#
In the app: Settings → Import from Logseq.
- Choose the graph's folder, or a .zip of that folder. Both kinds of Logseq graph work: a classic
file graph (the folder with
pages/andjournals/) and a DB-version graph (the folder withdb.sqlite, with Logseq's Markdown Mirror turned on). On a phone, zip it first (Files → long-press → Compress) and choose the zip. - Choose where it goes: a new graph (give it a name), or this graph if it is still empty.
- Watch it upload, unpack and import. At the end you see how many pages, journals, blocks and
images came across, and anything the importer noticed: block references that point nowhere,
images missing from
assets/, and the date format your journals used. - Open the new graph.
Your Logseq folder is only read, and only what the importer reads is sent: pages/, journals/,
assets/ and logseq/config.edn, or for a DB-version graph db.sqlite, mirror/markdown/ and
assets/. Backups and the rest stay behind. Importing needs the server's owner session: the
desktop app, or a browser on the server's own machine (a paired phone cannot start one). The largest
upload the server takes is 1 GB; serve --import-max-mb <n> changes that. A graph that lives only
on a phone ("Just this device") cannot import on its own: import on a server, then add that graph.
From a terminal instead (a classic file graph's folder, or a DB-version graph's folder, the one
with db.sqlite):
pnpm nooklet import ~/notes/my-logseq-graph
Importing before the first serve works too. Running it again skips pages that already exist.
Importing from Logseq covers which folder to pick and what carries over.
Recommended: a server on your tailnet#
This puts the server on a machine that stays on (a home server, a small VPS, a Mac mini), reachable only from devices on your Tailscale network, over HTTPS with a real certificate. Every device gets its own token.
-
In the Tailscale admin console, turn on MagicDNS and HTTPS certificates.
-
On the server machine, start nooklet on loopback, allow the tailnet name, and turn off the automatic local token (Tailscale's proxy connects from loopback, so without this flag every tailnet device could look like "this machine"):
pnpm nooklet serve --data ~/nooklet-data \ --allow-host <machine>.<your-tailnet>.ts.net \ --no-loopback-token -
Put Tailscale's HTTPS proxy in front of it:
tailscale serve --bg --https=443 http://127.0.0.1:6100Flags differ between Tailscale versions; check
tailscale serve --help. Do not usetailscale funnel, which publishes the server to the internet. -
From another tailnet device, check it:
curl https://<machine>.<your-tailnet>.ts.net/healthz # {"name":"nooklet","status":"ok"}A
403that saysHost "…" is not allowednames the exact--allow-hostvalue to add. -
Mint one token per device (next section) and connect each device to
https://<machine>.<your-tailnet>.ts.net.
To run it in Docker or Kubernetes instead, see Self-hosting.
Tokens#
Every device and every agent gets its own token. The server prints it once and stores only a hash.
# a phone or laptop that syncs
pnpm nooklet token create --label phone --scope write --sync
# an agent that reads and writes over MCP or HTTP, no sync
pnpm nooklet token create --label claude --scope write
pnpm nooklet token list
pnpm nooklet token revoke <token-id>
Add --data <dir> if the server does not use the default data directory, and --graph <id> for a
graph other than default. A device needs --scope write --sync to edit and sync. See
Security for what each scope allows.
Pairing link#
--link <address> also prints a link that sets up the iOS app in one tap:
pnpm nooklet token create --label phone --scope write --sync \
--link https://<machine>.<your-tailnet>.ts.net
nooklet://connect?url=https%3A%2F%2F<machine>.<your-tailnet>.ts.net%2Fg%2Fdefault&token=nk_…
Open it on the phone (AirDrop it in a note, or paste it into Safari's address bar). The app shows a "Connect to this server?" screen with the address and connects only when you tap Connect. It adds the server graph to the list and keeps any local-only graphs.
The link contains the token. Anyone who sees it can use the graph until you revoke it, and it lingers in clipboards, notes, chat and screenshots. Delete it after use. There is no QR code yet.
The web app#
Open the server's address in a browser. On a device other than the server, the app asks for a
token: paste one minted with --scope write --sync.
The address must be https://…, or http://127.0.0.1 / http://localhost on the server machine
itself. Over plain http:// to another machine the app shows a page explaining that it needs a
secure context, and stops.
The web app can be installed as a PWA from the browser's menu.
The desktop app#
Download it from the Releases page, or build it from source. None of the downloads is code-signed yet, so each OS warns the first time:
-
macOS (
nooklet-<version>-macos-arm64.dmgfor Apple Silicon,-macos-x64.dmgfor Intel). Open the.dmgand drag nooklet to Applications. Because the app is not signed or notarized, macOS says it "is damaged and can't be opened" or "cannot be verified". Clear the download quarantine once:xattr -dr com.apple.quarantine /Applications/nooklet.appOn some macOS versions right-click → Open → Open works instead, or System Settings → Privacy & Security → Open Anyway after the first refused launch. The Intel build is untested.
-
macOS with Homebrew: see Homebrew below.
-
Linux (
-linux-x64.AppImageor.deb). Built in CI, not yet tested by the maintainer. AppImage:chmod +x nooklet-*.AppImage && ./nooklet-*.AppImage. Debian/Ubuntu:sudo apt install ./nooklet-*.deb. Both need WebKitGTK 4.1, which current Ubuntu and Debian ship. -
Windows (
-windows-x64-setup.exe). Built in CI, not yet tested by the maintainer. SmartScreen shows "Windows protected your PC": More info → Run anyway. It installs for the current user, no administrator needed.
Homebrew (macOS)#
brew install --cask hnykda/nooklet/nooklet
brew trust hnykda/nooklet # once, so `brew upgrade` can load it
xattr -dr com.apple.quarantine /Applications/nooklet.app # after every install and upgrade
This installs the same .dmg as the Releases page (Apple Silicon or Intel, picked for you) and a
nooklet command: the app's own bundled server, so nooklet import ~/notes-graph,
nooklet mcp --stdio and nooklet token create work without a source checkout. Both use
~/.nooklet, the same data as the app.
What to know:
- It is unsigned, and Homebrew will not hide that. The cask is in our own tap rather than
Homebrew's main cask repository because Homebrew no longer accepts apps that fail Gatekeeper
there, and Homebrew removed its
--no-quarantineoption. macOS therefore treats the app as an unverified download after every install and every upgrade; thexattrline above (or Open Anyway in System Settings → Privacy & Security) is needed each time. Run it only if you trust the release you just installed;brew info --cask nookletshows where it came from. brew uninstall --cask --zap nookletmoves the app's settings, caches and window storage to the Trash. It never removes~/.nooklet, where your notes are.- The Intel build is untested, and Homebrew itself moved Intel Macs to its lowest support tier in September 2026.
To build it yourself you need Rust and the Tauri prerequisites in addition to the above:
pnpm desktop # development run
pnpm desktop:build # build nooklet.app and a .dmg under apps/desktop/src-tauri/target/
The app does not update itself yet. The version is at the bottom of the ? menu, which links to the
Releases page; download the newer build and replace the app. Your data stays where it is.
- Local mode ("This Mac"). The app starts its own bundled server on
127.0.0.1:6100, using~/.nooklet/default(or$NOOKLET_DATA). If something already answers on 6100, the app uses that server instead.NOOKLET_PORTmoves it to another port. - A remote server. Menu → Switch Server… → Add a server → the server's
https://address. The window then loads the app from that server and asks for a token.
The iOS app#
There is no App Store build. You build it with Xcode on a Mac and install it on your own phone. A free Apple ID works; apps it signs expire after 7 days and need a rebuild. Build and install nooklet on your iPhone has the full walkthrough and fixes for the usual signing errors.
pnpm ios:sync # builds the web client and copies it into the Xcode project
pnpm ios:open # opens apps/web/ios/App/App.xcodeproj
In Xcode:
- Settings → Accounts: add your Apple ID.
- The App target → Signing & Capabilities → tick Automatically manage signing, pick your team. If Xcode says the bundle id is taken, change it to something unique.
- Connect the iPhone, choose it as the run destination, and turn on Developer Mode on the phone (Settings → Privacy & Security).
- Run. The first time, trust your developer certificate on the phone (Settings → General → VPN & Device Management), then run again.
After a code change, run pnpm ios:sync again before building; Xcode only bundles what that step
copied.
In the app, choose Just this device for a local-only graph, or Sync with a server and enter
the server address and a token, or open a pairing link. The server address can be bare
(https://host); the app adds /g/default.
If the phone reaches the server over the local network instead of Tailscale, iOS asks once whether nooklet may find devices on your local network. Allow it, or the connection times out. You can change it later in Settings → Privacy & Security → Local Network.
Android (experimental)#
The Android app has never been run on a device or an emulator by the maintainer. It is generated from the same Capacitor project as the iOS app and the release workflow builds it, so it should work in principle, but nobody has checked. If you try it, please send a platform report, whether it works or not.
Install the APK#
- Download
nooklet-<version>-android-experimental.apkfrom the Releases page on the phone. (A file ending in-debug.apkmeans the release was built without the signing key: it installs the same way, but a later signed APK cannot update it in place. Uninstall it first, which deletes its local data.) - Open it. Android asks to allow installs from that app (your browser or file manager): allow it, then Install. Play Protect may warn about an app from an unknown developer; Install anyway.
- Optionally compare the file against
SHA256SUMSfrom the same release first.
What should work#
The same web client as iOS and the desktop apps, so in principle everything the features page lists:
- Just this device: a local-only graph stored in the app.
- Sync with a server: enter the server address and a token, as on iOS. Prefer an
https://address (Tailscale or a TLS proxy). Plainhttp://192.168.x.x:6100is allowed by this build for home-network servers, but the token and your notes then cross that network unencrypted. nooklet://links, including pairing links (token create --link).
What is likely rough#
- The keyboard toolbar and the space above the keyboard. The iOS insets were tuned on iOS; Android reports keyboard height differently.
- The back button. Nothing handles it yet; it probably closes the app instead of going back.
- Storage durability. The work that keeps a local-only graph safe when the app is backgrounded or storage is evicted (B-573: reopen on resume, a native-filesystem checkpoint) lives in the shared Capacitor code, so it runs on Android too, but it was designed around iOS's behaviour and only ever tested there. Keep anything important synced to a server.
- Background and resume, which on iOS needed several fixes.
- Cleartext HTTP to a LAN server relies on two settings that have never been exercised on a
device (
network_security_config.xml,allowMixedContent); live sync overws://may fail even where page loads work.
Report what you find#
Use the platform report
template. Its checklist covers the same checks the iOS app was tested with. The most useful extra
detail is the WebView console: enable USB debugging, connect the phone, open chrome://inspect in
desktop Chrome, and inspect the nooklet WebView. The sync engine runs in a Worker, listed
separately.
Build it from source#
You need Android Studio (or JDK 21 and the Android SDK) in addition to the above.
pnpm android:sync # builds the web client and copies it into apps/web/android
pnpm android:open # opens the project in Android Studio
Run it on a device or emulator from Android Studio, or build an APK on the command line with
cd apps/web/android && ./gradlew assembleDebug (output in app/build/outputs/apk/debug/).
Check it works#
- Type on one device; it appears on the other within a few seconds.
- Turn one device's network off, edit, turn it back on; the edits arrive.
pnpm nooklet verify --data <dir>on the server replays the op log and should printOK.
Next: Self-hosting for Docker, Kubernetes, proxies and backups, or For AI agents to connect Claude Code.