Skip to content

feat(1password): read host passwords from 1Password, use its SSH agent - #12

Merged
Sniphs98 merged 9 commits into
mainfrom
feat/1password-rdp
Sep 26, 2026
Merged

Sniphs98 merged 9 commits into
mainfrom
feat/1password-rdp

Conversation

@Sniphs98

Copy link
Copy Markdown
Owner
  • A host can carry a 1Password secret reference (op://vault/item/field,
    new "1Password reference" field in the host form, password_ref in
    hosts.toml) instead of a stored password. The app reads it with the
    1Password CLI (op read) right before connecting — before, not during, the
    handshake, since a Windows Hello / Touch ID prompt can outlast the connect
    timeout — keeps it in memory for 10 minutes, and never writes it to disk.
    If it can't be read, the keys still get their turn and the failure message
    says what 1Password reported.
  • SSH keys: on macOS and Linux the app now falls back to 1Password's agent
    socket when SSH_AUTH_SOCK isn't set (GUI apps on macOS usually don't get
    it). On Windows 1Password's agent already serves the pipe the app uses.
  • Key setup that turns password login off drops the reference too, so no
    pointless 1Password prompt follows every connect.
  • README: a "Using 1Password" section.

Tested against the SSH test container with a stand-in op (no 1Password in
CI): login through a reference works, an unreadable reference fails with
1Password's reason. Unit tests cover the reference format, the CLI call and
its error cases, hosts.toml and the host form.

Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com

What and why

How it was tested

Checklist

  • PR title is a Conventional Commit (it decides the release)
  • npm run check and npm test pass
  • Tests added or updated for the change
  • README updated if the change is user-visible

Sniphs98 and others added 9 commits September 26, 2026 10:13
- A host can carry a 1Password secret reference (op://vault/item/field,
  new "1Password reference" field in the host form, `password_ref` in
  hosts.toml) instead of a stored password. The app reads it with the
  1Password CLI (`op read`) right before connecting — before, not during, the
  handshake, since a Windows Hello / Touch ID prompt can outlast the connect
  timeout — keeps it in memory for 10 minutes, and never writes it to disk.
  If it can't be read, the keys still get their turn and the failure message
  says what 1Password reported.
- SSH keys: on macOS and Linux the app now falls back to 1Password's agent
  socket when SSH_AUTH_SOCK isn't set (GUI apps on macOS usually don't get
  it). On Windows 1Password's agent already serves the pipe the app uses.
- Key setup that turns password login off drops the reference too, so no
  pointless 1Password prompt follows every connect.
- README: a "Using 1Password" section.

Tested against the SSH test container with a stand-in `op` (no 1Password in
CI): login through a reference works, an unreadable reference fails with
1Password's reason. Unit tests cover the reference format, the CLI call and
its error cases, hosts.toml and the host form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ject]"

IPC handlers threw a plain { message } object. Electron hands a rejected
invoke only the thrown value's toString(), so in the real app every error
from the main process — a terminal or SFTP that couldn't open, a remote
desktop that couldn't connect, 1Password's reason — reached the UI as
"Error invoking remote method 'x': [object Object]". The e2e stubs pass the
object through untouched, which is why the tests never saw it.

Handlers now throw a real Error, and the renderer unwraps Electron's
"Error invoking remote method" prefix, so the message itself shows. Checked
in the real app: "unknown host: no-such-host" instead of "[object Object]".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A remote desktop connection gets the same "1Password reference" field as a
host (op://vault/item/field): its password is read from 1Password when
connecting — for the in-app tab and for mstsc / FreeRDP alike — instead of
being stored. A reference wins over a stored password; a password typed in
the tab wins over both. Tiles show a 1Password badge.

SSH and remote desktop share one 10-minute cache of what 1Password answered,
so one reference asks once however it's used. The reference check is shared
by both forms.

Tested in the real app with a stand-in `op` on PATH: the right reference is
read and used to connect; a wrong one shows 1Password's own message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t it

Once a 1Password reference is entered in a host or a remote desktop
connection, the form checks for the CLI the reference needs. Missing, it
shows the install command for this OS (winget / brew; the setup guide on
Linux) with a Copy button, the one setting to turn on in the 1Password
app, and "Check again"; installed, it shows the CLI's version.

`op` is also looked for where winget, Homebrew and the installers put it
when it isn't on the app's PATH — a CLI just installed with winget is
found without restarting the app, and on macOS an app started from the
Finder finds Homebrew's.

Tested in the real app: without op the hint and command show and Copy
copies it; with op in WinGet's Links folder but not on PATH, it is found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p screen

Closing the active tab always fell back to the SSH dashboard, even with the
sidebar in Remote Desktop mode. It now goes to the current mode's home screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…assword

Instead of a separate "1Password reference" field for the password, every
credential field in the host and remote desktop editors has its own 1Password
switch next to its label: hostname, user and password can each be typed or
read from 1Password when connecting.

Hostname and user keep their reference in the field itself (an address never
starts with op://); the password's still goes in passwordRef, since the stored
password never travels back to the form. Existing profiles open with the
password switch on, so nothing needs migrating.

Resolved in one place per protocol, right before dialling: resolveConnection
for RDP (launch and embedded), resolveHostAddress per SSH hop and for the TCP
port check. Certificates stay keyed by the saved address. Tiles, cards and the
palette show a reference as its item, ‹web-1›, and the badge says what comes
from 1Password.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The port (hosts and remote desktop) and the domain (remote desktop) get the
same 1Password switch as address, user and password. The domain keeps its
reference in the field; the port, a number on disk, gets portRef / port_ref
beside it and keeps the default, so older builds still read the file.

resolvePort reads and checks it (1-65535) without echoing what 1Password
returned. The TCP port check probes the resolved port unless it has one of
its own. Tiles and cards leave out parts from the same item as the address,
so a profile kept in one item shows as just ‹item›.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rd copies them with

"Copy Secret Reference" wraps a reference whose vault, item or field name has a
space in quotes: "op://IT/e-HPV one/Benutzername". The form refused both the
quotes and the spaces. Names may now hold spaces (not at either end), and a
reference pasted in quotes is saved without them. The backend accepts and
strips them too, so op gets the bare reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The default path gets the same 1Password switch, the reference kept in the
field. The backend reads it before starting the shell there; the terminal tab
no longer types a cd for a reference; the SFTP browser asks the backend
(sftp_default_path) for the path to open, falling back to / and saying why.
"Set as default path" in SFTP saves a plain path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Sniphs98
Sniphs98 merged commit a8abbb0 into main Sep 26, 2026
9 checks passed
@Sniphs98
Sniphs98 deleted the feat/1password-rdp branch September 26, 2026 13:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant