Peridot
Peridot is a small app with a large responsibility: it keeps a Nostr signing key in one place, lets other clients ask for signatures through NIP-46, and turns every sensitive request into a decision you can actually inspect.
A signer you keep on your desk
Peridot is not trying to become your timeline. That is the first thing to understand, because it changes how you judge it. You do not open Peridot to read notes, post a photo, join a group or shop for something. You open it because your Nostr identity is made of cryptographic keys, and every real client sooner or later wants a signature from that key.
The official README says the whole idea in one compact line: "Nostr bunker for desktop. Protect your Nostr identity, verify requests before the apps get it." That is not a slogan wrapped around a feed. It is a product boundary. Peridot sits between your private key and the apps that want to act as you. A client can prepare a note, a profile update, a follow list, an encrypted message operation or another Nostr action. Peridot is the place where the request lands before your identity endorses it.
That sounds technical, but the human situation is easy to recognize. You find a new Nostr client. It looks promising. The old habit would be to paste your `nsec` into it, hope the code is honest, hope the deployment is clean, hope the browser has not been tampered with, and hope you will remember where you pasted that key later. Peridot offers a more adult rhythm: keep the key in a signer, pair the app, review the requests, revoke or narrow trust when needed.
In a mature Nostr setup, that matters as much as the client itself. A social network account can usually be recovered by email. A Nostr identity cannot be recovered by a help desk. If your private key is copied, the copy is enough. The thief does not need your password reset inbox. The thief has your signing authority. A desktop bunker is one attempt to make that authority less casual.
Why a bunker exists at all
NIP-46, the Nostr Remote Signing specification, gives the mental model. It separates three roles that are often collapsed in beginner explanations: the user, the client and the remote signer. The client is the app you are using. The remote signer, also called the bunker, is the thing that controls the user keypair and answers signing requests. The client can be lively, experimental and replaceable. The signer should be boring, deliberate and hard to trick.
The protocol does not say "trust every app less because the ecosystem is scary." It says something more practical: private keys should be exposed to as few systems as possible because every extra app, operating system and device expands the attack surface. That sentence is the philosophical core of Peridot. The fewer places your `nsec` touches, the fewer places can lose it.
Remote signing works through Nostr itself. NIP-46 uses `kind: 24133` request and response events. The client generates its own session keypair, sends encrypted JSON-RPC-like messages to the signer, and receives encrypted responses back. It may ask for `get_public_key`, `sign_event`, `ping`, `get_relays`, NIP-04 encrypt/decrypt and NIP-44 encrypt/decrypt. Permissions can be broad, or they can be narrowed to a method and even to a specific event kind, such as `sign_event:1` or `sign_event:30023`.
Peridot turns that abstract protocol into a visible desktop workflow. It uses the Dart `nostr_bunker` package, which is explicitly built so an app can act as a bunker and sign events for other Nostr apps. It also uses `ndk`, the Dart Nostr Development Kit, for the Nostr network layer. In other words, Peridot is not inventing a private protocol around Nostr. It is building a user-facing shell around the signer pattern the ecosystem already has.
The first account is the first decision
When Peridot starts without an account, its route middleware sends you to the private-key screen. That is a small implementation detail with a real product consequence: the app knows it has no job until it controls at least one account. You can create an account, import an `nsec`, paste a hex private key or import an encrypted `ncryptsec` key. The `add_privkey` controller decodes `nsec` through NIP-19, decrypts `ncryptsec` through the `nip49` package, and derives the public key from the private key through BIP-340.
NIP-49 is worth pausing on. It defines password-based private-key encryption for Nostr and produces the `ncryptsec` format. Peridot uses it in both directions: import can decrypt an `ncryptsec` with a password, and backup can encrypt a private key into `ncryptsec` before copying it out. The same backup page also offers an unencrypted `nsec` copy, but it labels the secure path as a password-protected backup and the unprotected path as insecure. That distinction is not ornamental. It tells you the app is willing to show dangerous power, but does not pretend the dangerous path is harmless.
Inside the repository, private keys are written through `FlutterSecureStorage` under a `private_keys` storage key. Peridot stores apps, requests, relays and account metadata in Sembast, with `sembast_web` present for the web build. This is not a hardware wallet. It is a desktop and web Flutter app using platform storage. That means the trust boundary is your machine, your browser if you use the web version, and the app code. It is a meaningful improvement over spraying an `nsec` into every client, but it is not magic key custody.
The honest reading is simple: Peridot is for people who want a practical signer they can understand today. If you need hardware isolation, audits, reproducible builds and a threat model fit for major funds or public figures, you will want a stricter stack. If your problem is "I keep trying Nostr apps and I hate pasting my secret key everywhere," Peridot is aimed directly at that pain.
Pairing is where the risk gets named
The add-application flow is the heart of the app. Peridot supports both NIP-46 connection styles you will meet in the wild. It can generate a `bunker://` URL for the account you choose, and it can accept a `nostrconnect://` URL from a client. The first flow starts from the signer: Peridot gives you the bunker URL, and you paste it into the client. The second flow starts from the client: the client gives you a Nostr Connect URL, and Peridot connects back to it.
This distinction is not academic. A `nostrconnect://` URL can carry the client pubkey, one or more relays, a required secret, optional permissions, a name, a URL and an image. The NIP says the secret is required so the client can avoid simple connection spoofing. Peridot's controller parses the URL with `NostrConnectUrl.fromUrl`, connects the app through the bunker, pulls the app name when one is present, and then drops you into configuration.
That configuration step is where Peridot becomes more than a relay bridge. The app shows the app name, requested permissions and trust level. You can toggle individual permissions. You can choose between always asking, allowing common requests, or fully trusting the app. Those are not philosophical labels. They change whether Peridot interrupts you, quietly signs common operations, or allows everything from that app.
Notice the shape of the decision: Peridot does not ask "do you trust Nostr?" It asks whether this application, for this account, with these requested permissions, should be allowed to do this much. That is a much better question. Protocol ideology is vague. Per-app authority is something you can reason about.
After finishing the setup, Peridot enables the app, saves the bunker state and processes pending requests that match permissions it can auto-process. That last bit matters. If a client was already knocking during setup, you do not have to start over; Peridot can take the newly approved rules and apply them to waiting requests.
The request screen is the product
A signer lives or dies in the request view. That is where the user either understands what is being asked, or gets trained to click through alerts like a bored administrator. Peridot's request page pulls the saved `BunkerRequest`, finds the connected app, shows the app name, status, account picture, timestamp, command label and parameters. If the command is `sign_event`, it tries to parse the event JSON and show the event kind in human language.
That kind translation is one of the most useful details in the code. A raw Nostr event kind is just a number. Peridot maps many of them to names, including user metadata, short text notes, follows, direct messages, long-form content, application-specific data, Nostr Connect events, software applications, wiki articles, groups and more. If a request is about kind 3, the app even has a specialized widget for follow-list changes. Release v1.2.0 called out a simplified view to see your follow update before signing, which is exactly the kind of feature a signer should care about.
The action buttons are blunt in the best way: allow once, reject once, allow forever, reject forever. For blocked or processed requests, the available actions change. The controller persists those choices, updates the app's permission state and can process or block matching pending requests in bulk. A single decision can therefore become a rule, but only after you say so.
Peridot also exposes two warnings that tell you the developer has looked beyond the happy path. If a request does not use NIP-44, the page shows a deprecated encryption warning. If the request's bunker pubkey equals the user's pubkey, it warns about metadata leakage, telling the user that people can know they use a bunker and when they use it. That is a rare kind of product honesty. It does not pretend remote signing erases all metadata. It surfaces the awkward part right where the signature decision happens.
The result is a signer with a memory. Requests are stored as pending, blocked or processed. Apps show pending and blocked counts. There is a logs tab, a requests tab and a per-app management page. You can sort apps by name or last used, filter by account, rename apps, switch which account an app is connected to, remove an app, delete requests and delete logs. For a tool whose main job is "hold the line before signing," that recordkeeping is not busywork. It is how you audit your own habits.
Storage, relays and the desktop habit
Peridot is called a desktop bunker, and the desktop part matters. The repository contains Android, iOS, Linux, macOS, web and Windows directories because Flutter gives the project a cross-platform shape, but the product story points most clearly at a persistent signer you keep running near your main work environment. The settings controller uses `launch_at_startup`, so Peridot can launch when the computer starts. The notification service uses `flutter_local_notifications` across Android, iOS, macOS, Linux and Windows, so permission requests can reach you without forcing you to stare at the app all day.
Relays are the other part of the habit. NIP-46 requests travel over relays, and the signer should remain in control of which relays are used for the connection. Peridot has a default bunker relay settings view, lets you add relay URLs, validates that they use `wss://` or `ws://`, and prevents you from removing the last relay. That last behavior looks humble, but it is a good product instinct. A remote signer with no relay path is just a locked door in an empty hallway.
The app stores `app_last_used`, so last-used sorting is not faked by UI order. It saves apps, relays, user public keys and private keys as part of the bunker state. It can remove apps and cascade request cleanup for that app's pubkey. It can remove accounts and send you back to the account import screen if no accounts remain. These are not glamorous features, but they are what make a signer manageable after the first day.
There is also a privacy setting that deserves attention: remove client tag. In the manage-app screen, Peridot can automatically remove the client tag from events before signing for an app. Client tags can be useful for provenance and ecosystem analytics, but they can also leak app usage patterns. Peridot does not settle that debate for you. It gives you a switch at the app level.
What the code says about the build
The public project trail is compact but useful. The GitHub repository is `nogringo/peridot`, owned by the GitHub user `nogringo`. The repo was created on September 12, 2025, pushed on January 28, 2026, and was listed as updated on April 21, 2026 when checked. It has 14 stars, 1 fork and 0 open issues. The GitHub page shows Dart as the main language and topics for `bunker`, `nip46` and `nostr`. No repository license is visible through the GitHub API, so readers should not treat Peridot itself as having a stated license unless the project adds one.
The `pubspec.yaml` on master says `version: 1.3.0` and `Dart SDK ^3.10.7`. The dependency list tells you a lot about the app: Flutter for the UI, GetX for routing and state, `nostr_bunker` for NIP-46 bunker behavior, `ndk` for Nostr networking, `nip49` for encrypted private keys, `flutter_secure_storage` for local private-key storage, Sembast for app/request storage, `launch_at_startup` for desktop persistence, `flutter_local_notifications` for request alerts, `nostr_widgets` for Nostr profile UI, and `toastification` for feedback.
The latest GitHub release visible on the repository page is v1.2.0, published on November 14, 2025. Its assets include Android AAB and APK, Linux AppImage, Debian package and zip, a web zip, a Windows portable zip and `Peridot.dmg`. Earlier releases show a fast sprint from v0.1.0 on September 13, 2025 through v1.0.0 in early November. Release notes mention notifications in v0.2.0, a Nostr Connect fix and UI feedback in v0.4.0, a new UI with fewer bugs in v1.0.0, and the follow update view in v1.2.0. The master branch version being ahead at 1.3.0 tells you development moved beyond the latest packaged release, but it does not mean there is a v1.3.0 release asset yet.
The repository has GitHub Actions workflows for web deployment to GitHub Pages, Windows builds and macOS builds. The Pages workflow builds Flutter web with a base href for the repository name. The macOS workflow builds a release app and attempts to package a DMG. The Windows workflow builds a release app and creates a portable zip. There is also a `zapstore.yaml` pointing at the GitHub repository and matching APK assets, which signals the project expects Android distribution through Zapstore-style release metadata as well.
The app is localized in English, German, Spanish, French, Japanese, Russian and Chinese resource files. That does not prove broad adoption, but it does prove the developer thought about Peridot as more than a one-language experiment. For a signer, that matters. Trust prompts are not a place where you want users guessing through foreign UI.
What to admire and what to watch
The strongest thing about Peridot is that it gives the right problem a small surface. Nostr does not need every signer to become a social app. It needs signers that make authority legible. Peridot's Applications, Requests and Logs views are plain, but plain is a virtue here. A request to sign is not a place for decorative fog. You want to see the app, the account, the command, the event kind, the parameters and your choices.
The second strength is that Peridot treats trust as adjustable. Always ask, allow common actions, fully trust, permission toggles, app enable/disable, remove client tag, allow once, reject forever: these controls match the way real people actually learn. You may trust a client after weeks of use. You may trust it for short notes but not encrypted message decryption. You may want to block a command forever after one suspicious request. A good signer has to let trust become more specific over time.
The caution is that Peridot is still young. Fourteen stars and one fork is not a criticism, but it is context. A security-adjacent tool with a small public footprint deserves careful handling. Use small test accounts first. Read the request screens. Check release assets. Watch whether issues, releases and commits keep moving. Do not confuse "remote signing" with perfect safety. A remote signer reduces key exposure; it does not remove the need to understand the app you pair, the relays you use, the machine that holds the key or the metadata left by your actions.
The other caution is platform reality. The README says the macOS app is not code-signed and explains how to open it through the warning dialog or Privacy & Security settings. That is normal for many small open-source projects, but it is still something a careful user should notice. If your operating system says an app is unsigned, the right response is not panic. It is verification: check the repo, release page, hashes when available, maintainer trail and whether you are comfortable running that binary.
Read Peridot as a workbench tool. It is not glamorous, and it should not be. The best version of this app is the one you forget about until a client asks to sign something important. Then it should interrupt you clearly, show you the shape of the request, and let you say no without drama. That is the quiet part of Nostr sovereignty: not the poster sentence, but the button you understand before you press it.
Sources worth opening
Peridot is small enough that the best sources are the project itself, its code, its release trail and the NIPs it implements. Start here if you want to verify the claims or build your own signer mental model.
- Peridot official web app
- nogringo/peridot GitHub repository
- Peridot README
- Peridot pubspec.yaml
- Peridot releases
- Peridot Zapstore metadata
- Repository controller
- Add application controller
- App configuration view
- Request controller
- Request page
- Request action buttons
- Private-key import controller
- Backup account controller
- Settings controller
- Notification service
- English localization file
- GitHub Pages workflow
- Windows build workflow
- macOS build workflow
- NIP-46: Nostr Remote Signing
- NIP-49: Private Key Encryption
- NIP-44: Encrypted Payloads
- nostr_bunker Dart package
- nip49 Dart package
- NDK Dart package
- NostrApps listing





