Hisar Desktop
Wails v3 (pinned beta, see WAILS_VERSION in the root Makefile) with a Vue 3 + TypeScript frontend.
| Path | What |
|---|---|
main.go and the other root files | The Wails app: main.go wires the services, adapters.go the clipboard, notifications and file dialogs, windows.go the main and approval windows, events.go the event names, tray.go the menu bar item. The backend bound to the UI is core/app, shared with the iPhone |
agent/ | SSH agent in the app: the approval queue for SSH, command-line and coding-agent requests (Touch ID, master password or the iPhone), ~/.ssh/config setup, agent status. No Wails import |
appinfo/ | Finds the app behind a process for the approval window: its name, icon and code signature (cgo on macOS) |
browser/ | The browser extension's native messaging host, the app's socket for it and the host manifests; the messages are in core/browserprotocol |
commandline/ | Installing the bundled hisar on the PATH and Settings → Command-line tool; the CLI socket server is core/clisocket |
loginitem/ | Settings → "Open Hisar at login" on top of background/ |
biometric/ | Touch ID and the macOS Keychain (cgo) |
updater/ | Sparkle bridge for automatic updates (cgo, build tag sparkle; packaged macOS builds only) |
background/ | Open at login (SMAppService on macOS through cgo, an XDG autostart file on Linux), launch-at-login detection and the Dock icon. No Wails import |
systemidle/, windowplacement/ | Idle time for auto-lock, and placing the approval window (cgo on macOS) |
cmd/ | hisar-browser-host, the native messaging host binary, and hisar-update-key |
frontend/src | UI: api.ts (only file that calls the bindings), store.ts (state and actions), components/ |
frontend/bindings | Generated by wails3 generate bindings during every build; committed, and CI fails if a build changes them |
build/ | Platform build config (macOS, Linux) |
Build and run on macOS
Requires Go 1.26+, Node 24 and Xcode command line tools.
make install-wails # installs the pinned wails3 CLI into ~/go/bin
make package-desktop # builds desktop/bin/Hisar.app (ad-hoc signed)
open desktop/bin/Hisar.app
For development with hot reload: cd desktop && wails3 dev.
Touch ID wrapping keys use the macOS data-protection keychain. Ad-hoc builds can use password unlock but cannot set up
OS-protected Touch ID. For a team-signed local build, set HISAR_SIGNING_IDENTITY and
HISAR_MACOS_PROVISIONING_PROFILE to a matching macOS Developer ID profile. The profile must authorize team
589NVZWDWZ, the app's bundle identifier, and keychain group 589NVZWDWZ.<bundle identifier>. An iOS profile does not work.
The signing script checks the profile, embeds it, and emits the authorized entitlements. Password unlock migrates an old
wrapper only after a protected readback succeeds. A missing profile leaves password unlock available and shows the setup
error instead of claiming that Touch ID protection is active. Enrollment changes require password fallback and fresh setup.
For the printable kit QR smoke on macOS, run HISAR_TEST_KIT_PDF=/tmp/hisar-kit.pdf go test ./core/emergencykit -run TestGenerateProducesSinglePagePDF -count=1 from the repository root, then
swift scripts/test-emergency-kit-qr.swift /tmp/hisar-kit.pdf. This renders the actual PDF and decodes its QR with Vision.
It does not verify external camera applications or signed Touch ID behavior.
DMG and releases
make package-desktop-dmg VERSION=0.1.0 builds a universal (Apple silicon and Intel) Hisar.app with Sparkle embedded,
sets its version, and writes desktop/bin/Hisar-0.1.0.dmg with the usual drag-to-Applications window
(build/darwin/dmg-settings.py, background rendered from build/darwin/dmg-background.html). It needs
pip install dmgbuild==1.6.5. Versions are MAJOR.MINOR.PATCH (each part up to 999); CFBundleVersion is the build number
MAJOR × 1 000 000 + MINOR × 1 000 + PATCH (scripts/get-build-number.sh), so Sparkle sees every release as newer.
make package-desktop-update VERSION=0.1.0 SPARKLE_PRIVATE_KEY=<key file> [RELEASE_NOTES=<markdown file>] also writes the
Sparkle update: Hisar-0.1.0.zip, signed with the EdDSA private key, and update.json (signature, length, minimum macOS,
release notes). The script refuses a key that does not belong to SUPublicEDKey in build/darwin/Info.plist.
A release is a run of Actions → Release with the app desktop (CI and releases has the
steps). It tags desktop-<version> with the changes as the message, which becomes the release notes Sparkle shows, and
builds on a GitHub-hosted macOS runner and Linux runners. The GitHub prerelease desktop-<version> then has
Hisar-<version>.dmg, Hisar-<version>.zip, update-unpublished.json and the Linux packages, a download for the team.
Nobody gets it as an automatic update until the run's Publish job is approved, which renames the file to update.json and
marks the release as the latest. A custom build such as desktop-1.0.0-alpha-test has no zip or update and can't be
published. The build needs the repository secrets SPARKLE_PRIVATE_KEY; MACOS_SIGNING_CERTIFICATE (the team's
Developer ID Application .p12, base64), MACOS_SIGNING_CERTIFICATE_PASSWORD, and MACOS_PROVISIONING_PROFILE (a matching
macOS Developer ID provisioning profile, base64); and the App Store Connect API key that
notarizes the app and the DMG: APPLE_NOTARY_KEY (the .p8, base64), APPLE_NOTARY_KEY_ID and APPLE_NOTARY_ISSUER_ID.
Automatic updates
Design: Design §9.1.1. Sparkle (version and SHA-256 pinned in scripts/download-sparkle.sh, downloaded into
build/sparkle/ by packaged builds) checks <server>/api/v1/desktop/updates/appcast.xml on the server you are signed in
to, with your session, once a day, and downloads the update from the same server, which relays the published releases from
GitHub (Self-hosting); if the server has updates off, checks report that updates are not configured. Settings → General → Updates shows the
version, Check for updates, and Automatically check for updates. wails3 dev builds have no Sparkle and say so.
The EdDSA private key lives outside the repository (kept by the maintainer and in the SPARKLE_PRIVATE_KEY repository secret);
losing it means existing installs can no longer verify updates, so keep a backup. Preview: &settings=1&settingsSection=general,
&updates=development (build without Sparkle) or &updates=signed-out.
To work on the design without a server, run the UI in a browser with sample data:
cd desktop/frontend && npm run preview:ui
# http://127.0.0.1:5180/?state=signed-out | locked | unlocked
# extra parameters: &vault=team &item=sentry &editor=create|edit|secure-note|ssh-key &secretKey=1 &touchIdOffer=1 &search=git
# SSH: &item=raspberry (SSH key) &settings=1 &noTouchID=1 (in-app approval note) &compact=1 (compact approvals on) &agent=error &sshConfigured=1
# sharing: &sharing=1 (sharing panel; add &vault=work for the read-only view) &code=elif (security code) &remove=deniz
# settings: &settings=1 &invite=form|1 (invite form, or a sent invite) &revoke=mac-mini &settingsSection=general|security|ssh|developer|browser|people|devices|account|help &settingsSearch=<text>
# offline: &offline=1 (offline notice)
# import: &import=choose | preview | saving (stops halfway) | done | deleted, &importVault=team (target vault), &newItemMenu=1 (open the + menu)
The preview backend (src/preview.ts) is loaded with a dynamic import behind a build-time flag; npm run check:bundle (run in CI) fails if it ever ends up in the production bundle.
The app keeps its session in ~/Library/Application Support/hisar/desktop.json (override with HISAR_DESKTOP_STATE), the encrypted offline cache in desktop-cache.db next to it (override with HISAR_DESKTOP_CACHE), and the Secret Key in the macOS Keychain under the service hisar.
Right-click menus
Design: Design §9.1 (desktop: item menu).
- An item in the list (a vault's or Favorites): Open; Copy for its username, password, one-time code and main value (card number, license key, API secret, connection string, public key), each once the item's details have loaded; Open website (Open admin console for a server); Add to Favorites or Remove from Favorites; Share link…; and, in a vault the user can edit, Edit, Duplicate ("GitHub copy", without passkeys), Move to… (another vault the user can edit; the item keeps its ID) and Delete…, which asks first. Arrow keys move through the menu and Escape closes it.
- A field row in the detail panel offers the row's own buttons by name, such as Reveal password, Copy password and Copy secret reference for password, or Open website. With text selected in the row, the system's menu appears instead, with its Copy.
Item icons
Design: Design §4.5 (icons and website icons).
- Choose one in the editor: click the tile next to the title for Automatic, one of 30 presets in a color, or Upload image… (PNG, JPEG, GIF or WebP; Hisar crops and shrinks it to at most 128 pixels). The icon is saved in the encrypted item, so every device shows it.
- Website icons: a login without an icon of its own shows its website's, which this Mac asks the website for directly and keeps sealed in the offline cache. Settings → Appearance → Show website icons turns that off. Servers, databases and other types keep their own icon.
Sharing, people and devices
Design: Design §4.4 (vault keys and rotation), §5.1 (invites) and §5.6 (public key pinning).
- Share a vault: select a shared vault and press Share in the item list header. Everyone with access is listed with their role; owners can invite someone by email (owner, editor or reader), change roles and remove people. Editors and readers see the list read-only. Personal vaults show Private instead and cannot be shared.
- Removing someone re-encrypts the vault: the owner's device generates a new vault key, re-encrypts every item and wraps the new key only for the remaining members (
session.RemoveVaultMember). Copies the removed person already downloaded stay readable to them. - Key pinning: the first time this device shares with someone, it pins their signing key in
desktop.json(pins), like the CLI (localstate.State.CheckMemberPin). Sharing, changing a role or re-encrypting the vault stops if a pinned key changes. Each person has a security code (crypt.FormatKeyFingerprint: the first 8 bytes of SHA-256 of their Ed25519 signing key, as 4 groups of 4 hex digits); expand a member in the sharing panel or open Settings → People to see it, and compare it over a channel you trust. - Settings → People lists the people you share a vault with and their security codes, and the share invitations you sent and received. Anyone can Invite someone to Hisar by email. The app shows no account roles; the admin role only matters to the operator's
hisar-servercommand. - Settings → Devices lists the devices signed in to your account. Revoke signs another device out; to sign out this Mac, use Sign out in Settings → Account.
Signing in again: the Emergency Kit
Design: Design §6. The Emergency Kit (email, server and Secret Key) plus the master password is the only way to sign in again; there is no account recovery.
- Lost Secret Key: it is on the Emergency Kit. Settings → Security → Emergency Kit saves a new kit after the master password, and the sign-in screen says where to find it.
- Forgotten master password: nobody can recover it, including Hisar. The Create account form and the Secret Key screen after sign up say so.
Secure notes
Design: Design §4.5 (categories).
- + → Secure note opens an editor with the title, a large notes field and custom fields; there is no username, password or website. The item list shows the note icon and "Secure note".
- A note's view shows its notes first, with Copy and a secret reference (
hisar://<vault>/<note>/notes), then its custom fields. The browser extension never offers notes for filling. - A login with nothing but notes and custom fields, such as one imported before secure notes existed, shows Convert to secure note.
- Preview:
&item=recovery-codes(a note),&item=door-codes(a login that can be converted),&editor=secure-note.
Credit cards
Design: Design §4.5 (categories).
- + → Credit card opens a card editor: cardholder, number, type, expiry, security code, PIN and the billing address's street, city, region, postal code and country, then notes and custom fields. The number and expiry are formatted as you type (
4444 5555 5555 5555,01/30). The type (Visa, Mastercard, American Express…) follows the number until you pick another, with a hint when they disagree. The editor warns about a number that fails its checksum, an expiry it cannot read or an expired card, but saves anyway. - A card's view shows "Visa •••• 4242" and keeps the number, security code and PIN masked until revealed; each value has Copy and a secret reference. The item list shows the type and last four digits only.
- Cards import from 1Password (
.1pux) and Bitwarden JSON; a Bitwarden CSV leaves them out. - Preview:
&item=visa(an expired card),&editor=credit-card.
Identities
Design: Design §4.5 (categories).
- + → Identity opens an editor for the first, middle and last name, birth date (a date picker; an imported date that is not year first stays text), email, phone, the address's street, city, region, postal code and country, and the passport, driver's license and national ID numbers, which are masked until shown; then notes and custom fields.
- An identity's view shows the full name and lists each value with Copy and a secret reference; the document numbers are masked until revealed. The item list shows the name, or the email.
- Identities import from 1Password (Identity, Passport, Driver License, Social Security Number) and Bitwarden JSON; a Bitwarden CSV leaves them out.
- Preview:
&item=passport,&editor=identity.
API credentials
Design: Design §4.5 (categories) and §9.4.1 (secret references).
- + → API credential opens an editor for the service, key ID, secret (masked until shown), host and expiry (a date picker; an imported expiry that is not a date stays text); then notes and custom fields.
- The view lists each value with Copy and a secret reference, so
STRIPE_KEY=hisar://Work/Stripe/secretin a.envworks withhisar run. The secret is masked until revealed, and the expiry warns once it has passed. The item list shows the service and host, and search matches them. - 1Password API Credentials import as API credentials; their type, filename and valid-from date stay custom fields.
- Preview:
&vault=work&item=stripe,&editor=api-credential.
Addresses
Design: Design §4.5 (categories) and §9.2 (address autofill in the extension).
- + → Address opens an editor for the street (more than one line, such as an apartment on the second), city, region, postal code and country; then notes and custom fields. Identities and cards edit their address with the same fields.
- An identity or card saved before it had these fields keeps its one-line address as "Address (old format)" or "Billing address (old format)" while it is set; Use as street moves it into the street.
- The view lists each value with Copy and a secret reference. The item list shows the city and country, and search matches them and the street's first line.
- The browser extension fills address forms from addresses and from identities with an address, in the inline menu on an address field and in the popup's Addresses tab.
- Preview:
&vault=work&item=office,&editor=address.
Software licenses
Design: Design §4.5 (categories).
- + → Software license opens an editor for the product, version, license key (masked until shown), licensed-to name and email, purchase date (a date picker; an imported date in another form stays text) and download URL; then notes and custom fields.
- The view lists each value with Copy and a secret reference (
hisar://Personal/Sketch/license%20key). The key is masked until revealed, and the download URL opens in the browser. The item list shows the version and licensed-to name, and search matches the product, version, name and email. - 1Password Software Licenses import as software licenses, with the title as the product; company, publisher and order details stay custom fields.
- Preview:
&vault=work&item=sketch,&editor=software-license.
Servers
Design: Design §4.5 (categories) and §9.2 (browser extension).
- + → Server opens an editor for the host and port (warning about a port that is not 1–65535), the username, password and one-time password, the admin console URL and an SSH key from the same vault; then notes and custom fields. A host typed as
ssh://host:2222orhost:2222is split into host and port. - The view lists the host and port with Copy and a secret reference, the login fields as for a login, the admin console with Open in browser, and the linked SSH key, which opens; a key that was deleted shows as missing. The item list shows the host, port and username, and search matches the host and port, alone or as
host:port. - The browser extension offers a server's username and password on its admin console, as it does a login's.
- 1Password Servers import as servers. The admin console becomes the website only when it has no username or password of its own; otherwise the console URL and credentials stay custom fields, so the browser never fills the server's password on the console. Hosting details stay custom fields.
- Preview:
&vault=team&item=build-server,&editor=server.
Databases
Design: Design §4.5 (categories) and §9.4.1 (secret references).
- + → Database opens an editor for the engine (PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, MongoDB, Redis, or Other with any name), the host and port (warning about a port that is not 1–65535), the database name, the username and password, and connection options such as
sslmode=require; then notes and custom fields. - The view lists each value with Copy and a secret reference, then the Connection string, masked until revealed, with Copy and a secret reference (
hisar://Personal/Prod%20db/connection%20string). It is built from the fields, so it follows a new password. An Other engine has none; a custom field named "Connection string", such as a JDBC URL, then serves for Copy andhisar://…/connection string. The item list shows the engine, address and database name, and search matches any of them. - 1Password Databases import as databases. The SID becomes the database name when there is none, as for Oracle; otherwise it stays a custom field, like the alias.
- Preview:
&vault=team&item=staging-database,&editor=database.
Import from other password managers
Settings → General → Import…, or + → Import… in a vault. The parsing is core/importer (design: Design §9.7).
- Choose the format, or let Hisar detect it, then pick the file. Hisar reads 1Password
.1puxand CSV, unencrypted Bitwarden JSON and CSV, LastPass CSV, and browser CSV exports. The initial destination is Personal, even when you opened Import from a shared vault. - The preview counts logins, secure notes, SSH keys, credit cards, identities, API credentials and addresses, lists what is not imported and why (documents, archived items, passphrase-protected SSH keys), and lists items already in the vault, which are skipped unless you untick the box. A login matches on its title, username and website, a card on its title and last four digits, an identity on its title, name, email and first document number, an API credential on its title and key ID (or host, or secret when it has neither), and an address on its title, street, postal code and country. Nothing is saved before Import. Each 1Password vault or Bitwarden organization and collection set has its own destination selector. Leave private source groups in Personal unless you intend to share them. To use a shared destination, select it explicitly and review its current members and roles. The import never creates sharing grants or source vaults automatically.
- Items are saved in batches of 10 with a progress bar; if the connection drops, Try again continues where it stopped. Locking Hisar discards the parsed file. If destination membership changes, saving stops before the next item is committed. Choose Stop and keep the source file, then preview the source again to review the new access scope. Already saved items remain in their chosen vaults. Later sharing changes can still expose those saved items.
- The export holds every password in plain text, so Hisar offers to delete it (Delete file removes it directly, not through the Trash).
Browser extension bridge
The Hisar extension (extension/) fills and saves logins through the desktop app (design: Design §9.2).
Hisar.app/Contents/MacOS/hisar-browser-hostis the native messaging host. At launch the app writescom.tryhisar.hisar.jsonintoNativeMessagingHostsof every Chromium browser it knows (Chrome, Brave, Edge and their beta, dev, canary and nightly channels, Chromium, Arc, Dia, Vivaldi and Vivaldi Snapshot, Opera and Opera GX), whether or not it is installed, so a browser installed later finds the host at its first launch. Dia reads Chrome's folder rather than its own, so its entry points there. It allows only the extensionmmipiffoogblblocokimpemdopmdjghf. Settings → Browser lists the browsers that have run. A development build (wails3 dev) has no host next to its binary and registers nothing.- The host relays to the app on
~/.hisar/browser.sock(HISAR_BROWSER_SOCKEToverrides it for both). In the packaged app only that host binary may connect. If the app is not running, the host opens it in the background. - Pairing: clicking the extension's icon the first time makes Hisar show a 6-digit code (2 minutes, 5 attempts) to type into the extension. Settings → Browser lists paired browsers with Remove and has "Only fill on the exact host".
- Unlocking with the master password in the paired extension also updates the desktop window, so it shows the vault instead of a stale lock screen.
- Preview:
&settings=1&settingsSection=browser(registered and paired browsers),&browsers=none(nothing found),&browserPairing=1(pairing code).
SSH agent
Hisar is an SSH agent for the SSH keys in your vaults (design: Design §9.6).
-
Add a key: + → SSH key, then Generate (Ed25519, or RSA 4096 for old servers) or Import (paste an OpenSSH or PEM private key; a passphrase is only needed to import, the key is stored without it).
-
Copy the public key from the key's page (Copy public key) into GitHub or the server's
~/.ssh/authorized_keys. -
Settings → SSH & Git → Use Hisar for SSH. This backs up
~/.ssh/configto~/.ssh/config.hisar-backup-<timestamp>and adds at the top:# Added by Hisar: use the Hisar SSH agentHost *IdentityAgent "~/.hisar/agent.sock"# End of Hisar sectionRemove from SSH config takes the section out again. To try it without changing the config:
SSH_AUTH_SOCK=~/.hisar/agent.sock ssh-add -l.
The agent listens on ~/.hisar/agent.sock while the app runs (HISAR_SSH_AGENT_SOCKET overrides the path) and offers keys from all your vaults only while Hisar is unlocked. Each use of a key opens the approval dialog, which says who asks for what, like "Terminal wants to push to git.tryhisar.com", with the command and repository, and starts Touch ID at once ("or use your master password" instead). "Don't ask again for Terminal for 1 hour" is checked by default; commit signatures always ask, and requests forwarded from another computer (ssh -A) show a red warning and can only be allowed once. While Hisar is locked, the dialog unlocks it. Settings → SSH & Git → Compact SSH approvals shows a small card at the top right instead. Unanswered requests are denied after 60 seconds. Preview: ?window=ssh-approval&approval=1 (or =2 for two waiting, =signing for a commit signature), plus &approvalLocked=1 (Touch ID unlocks), &approvalPassword=1 (master password unlocks), &compact=1, &forwarded=1, &noTouchID=1.
Keeping Hisar running
ssh, git and commit signing cannot start Hisar: while the app is not running, ~/.hisar/agent.sock refuses connections and they
fail at once. So the app keeps itself available (design: Design §9.1.2):
- Settings → General → Open Hisar at login (on by default; registered once on the first launch, never again after you turn it off).
macOS uses
SMAppServiceand Settings shows what System Settings → General → Login Items says, with Open Login Items when macOS wants your approval; Linux writes~/.config/autostart/com.tryhisar.hisar.desktop. At login Hisar opens locked and without a window. - Closing the window keeps Hisar running in the menu bar (Open Hisar, Lock or Unlock…, Quit Hisar); the Dock icon shows only while the window is open. ⌘Q or Quit Hisar quits.
HISAR_DESKTOP_STATEalso separates the single-instance lock, so a development copy with its own state runs next to the installed app.
Command-line tool
Design: Design §9.4. The packaged app bundles the CLI as Hisar.app/Contents/Helpers/hisar (universal, signed with the
app, updated with it by Sparkle). Not Contents/MacOS/hisar: on the default case-insensitive file system that is the app's own Hisar.
- Settings → Developer tools → Install command-line tool links
hisarinto/opt/homebrew/binor/usr/local/bin(whichever exists and is writable first), else~/.local/bin, without an administrator password; Settings then shows a one-line PATH hint if that directory is not on yourPATH. It never replaces ahisarthat is not a link into aHisar.app. Uninstall removes only such links. - Let the command-line tool use Hisar (on by default) serves
~/.hisar/cli.sock(HISAR_CLI_SOCKEToverrides it for the app andhisar).hisarthen runs its commands through the app: the first command in a terminal opens the approval window ("hisar in Terminal wants to use Hisar") with Touch ID, which also unlocks a locked Hisar (or asks for the master password). The approval lasts until 10 minutes after that terminal's last command, or until Hisar locks or quits. Ahisarwithout a terminal (run by a GUI app) is approved per app whose code signature macOS verified, else for its one connection, and the window says which.hisar statusshows which mode it uses. - When the app is installed but not running,
hisarstarts it hidden (open -g … --args --hiddenon macOS, theHisarbinary with--hiddenon Linux), prints "Starting Hisar…" and waits up to 10 seconds for the socket. If it still does not answer,hisaruses its own sign-in when it has one, and otherwise stops with "Hisar isn't running. Open the Hisar app and try again."HISAR_NO_LAUNCH=1never starts the app, for scripts and CI. - Without an installed app, with the switch off, or with
hisar --standalone/HISAR_STANDALONE=1,hisarsigns in on its own as before. - Preview:
&settings=1&settingsSection=developerplus&commandLine=not-installed | conflict | path-hint | development | off; the approval window with?window=ssh-approval&approval=cli(add&cliLocked=1for Touch ID unlock,&cliPassword=1for the master password, which iscorrect horse batteryin the preview).
Coding agent approvals and website sign-in
See Design §9.4.2 and §9.4.5. Every coding-agent approval, including an SSH key request, requires Touch ID, the master password or a verified phone answer. Without Touch ID, the Mac dialog requires the master password. An Approve button alone would let an agent that controls the screen approve itself.
- A dialog that names secrets has a menu: Only this time (the default), For 1 hour, For 1 day or Always. The Touch ID
prompt names the choice (
allow "Hermes in Terminal" to sign in to https://tosunlab.com as user@example.com for 1 day); changing the menu or the login while it waits starts a new prompt. For omp, Claude Code, Codex and Hermes a longer choice covers that agent in every session that runs the same program: the code signature Hisar recognized it by, or else the path of its executable. When Bun or Node runs the agent as a script, the program is the interpreter together with the script's real path (its bin link resolved), or the SHA-256 of code given with-eor-p; another script, even one namedomp, is another program. Hermes' launcher runs its Python with-I -c, so its program is that Python together with the SHA-256 of the-ccode. When Hisar cannot tell the script (a relative path, an option before it,-m, a preload, loader or env-file option anywhere, a second-e, Python without-I), or the process only takes the agent's name, the dialog offers only the first two choices. Code the user's own files or environment add (abunfig.tomlpreload,NODE_OPTIONS, a replaced script) is outside what this tells apart. A grant is kept indesktop.json(sealedAgentGrants), sealed with the account's key and a random head that every change replaces. Hisar also keeps the head in a login keychain item, "Hisar agent grants". The item's default access list trusts the Hisar that created it. Reading it from another program prompts for Keychain access. Another program can overwrite or delete the head without asking, which only drops grants. A restart keeps grants whose sealed head matches the Keychain head. Grants written into the file by another program, an older copy restored over it, or a missing or unreadable head drop all saved grants. When the head store is unavailable, including on Linux, only the first two choices appear, and For 1 hour ends with the session or connection. The phone cannot choose a duration, so its approval is for this time only. - Settings → Developer tools → Coding agents → Allowed without asking lists every grant with who holds it (a harness, "this session" or "while connected"), its expiry and Revoke. Signing out ends them all. Session-scoped and connection-scoped grants also end with their session or connection. Locking keeps grants, but Hisar must unlock before using them. If a saved grant cannot be removed while locked or its removal cannot be saved, Hisar drops all saved grants and reports any storage error.
- Website sign-in (off by default, Hisar for Mac only): "Let agents sign in to websites in your browser". Turning it on asks
macOS for the Accessibility permission; while it is missing the row says so, with Open System Settings, and it checks again
when Hisar's window comes back to the front. An agent then calls
sign_inwith a browser window'spidandwindow_id(setup for Hermes, Claude Code and Codex: Command-line tool). Hisar signs in only in Brave, Google Chrome and Microsoft Edge, and refuses one started with remote debugging. It reads the page's address from the browser itself, finds the logins in the vaults available to agents saved for it, and asks: "Hermes in Terminal wants to sign in to https://tosunlab.com", with the browser, the login (a choice when several match, on this Mac only) and the menu. Only this time covers exactly that call: the agent's nextsign_in, even at the same website, asks again. A longer choice covers that login at exactly that website.cleanup-failedends that grant, or drops all saved grants if Hisar is locked or cannot save its removal. The sign-in tool returns login metadata, including the username, but no password or one-time code. Hisar fills and submits the login, then attempts to clear remaining entries and reports the cleanup evidence.fields-removedmeans fields left the accessibility tree while their document stayed, not that the page erased their values.cleanup-failedmeans Hisar could not confirm cleanup. Either can leave values after return with no Hisar-enforced retention limit. The 15-second post-submit deadline and separate five-second cleanup timeout are not exposure bounds. Accessibility calls can overrun them. A screenshot or recording can capture a one-time code while the page retains it. Browsers normally mask secure password fields with dots, but a page script or show-password control can reveal the value. Captures and page-script copies have no Hisar-enforced deletion limit. - Preview:
?window=ssh-approval&approval=sign-in(add&logins=2for a choice,&agentLocked=1for a locked Hisar,&noTouchID=1for the master password,&phone=1),&approval=agent-runand&approval=ompfor the menu, and&settings=1&settingsSection=developer&agentSessions=1&agentSignIn=on(or=noAccess) for Settings.
Raycast extension
Design: Design §9.4.3. raycast/ brings the 1Password Raycast extension's features to Hisar: Search Hisar, My Vaults,
One-Time Codes, Generate Password (quick and with options) and Lock Hisar, with copy and paste of usernames, passwords and one-time
codes, Open or Edit in Hisar and Share Item…, through the running app on ~/.hisar/cli.sock. It is not in the Raycast Store;
install it in development mode:
cd raycast && npm ci && npm run dev # registers "Hisar" in Raycast; it stays installed after you stop it with Ctrl-C
- The first search after Hisar unlocks opens the approval window: "Raycast wants to search Hisar and copy or paste passwords and one-time codes" (Touch ID, the master password or the iPhone). The approval lasts until Hisar locks. Settings → Developer tools → Command-line tool → Launchers lists approved apps with Revoke. The command-line switch must be on.
- Copies come from Hisar itself and are marked so clipboard managers skip them; they are cleared after 90 seconds. Paste reads the value into Raycast for that one paste. Share Item… asks in Hisar every time, and Open in Hisar brings the window forward on the item.
- The extension's preferences set the socket (default
~/.hisar/cli.sock, for a development copy of the app with its ownHISAR_CLI_SOCKET), the primary and secondary actions of Return and ⌘↩, and whether Raycast closes after a copy. - Preview:
?window=ssh-approval&approval=launcher(the launcher window),&approval=launcher-share(a share link from Raycast),&coverage=appor&coverage=connectionon theapproval=cliwindow, and&settings=1&settingsSection=developer&launchers=1for the Launchers row.
Tests
make test # every desktop package but the Wails main package, and core/app
make test-desktop-frontend # vue-tsc type-check and Vitest component tests
make test-end-to-end # includes the desktop backend against a real server