Building Silo Tray: the background companion

August 3, 20262 min readTechnical

The terminal is Silo’s management surface, but to use the extension, we need to have the broker active or unlocked or log into the shell. However, having to open the broker or shell whenever I need a password completion isn't ideal for me and so a better and more efficient way is to have a simple on/off button switch. This need can be solved with a tray.

Silo Tray is the small menu-bar or system-tray companion that keeps the local broker available without keeping a terminal window open. That way, I can easily turn it off when it's not needed too.

The job of the tray

The tray is intentionally narrow. It owns the desktop lifecycle, while the broker owns the unlocked vault session.

CODE
Silo Tray ├── starts a locked broker ├── reads broker status ├── exposes lock / unlock ├── opens the Silo shell └── quits the broker session

It does not store a second vault, sync credentials, or become another password database.

Process map

CODE
┌─────────────────┐ │ Silo Tray │ │ menu-bar UI │ └────────┬────────┘ │ local request ┌─────────────────┐ │ Silo Broker │ │ locked session │ └────────┬────────┘ │ unlocks ┌─────────────────┐ │ Encrypted vault │ │ local file │ └─────────────────┘

The browser follows a separate path:

CODE
Browser extension → native host → broker → approved username/password/TOTP

The browser never receives the master password.

Starting the locked broker

The tray starts a broker with no decrypted vault in memory:

rust
let broker = silo_broker::start_locked(args.vault.clone(), args.timeout)?;

That separation is important. A tray restart should not create a new vault format or duplicate the vault’s encryption logic. It only starts and controls the process responsible for the session.

The menu is status-driven:

CODE
Silo vault: locked Unlock Silo vault Open Silo vault Quit Silo vault

When the broker is unlocked, the menu changes to "Lock Silo vault". The tray asks the broker for status rather than guessing from its own state:

rust
let response = broker_request(Request::Status)?; let label = if response.unlocked == Some(true) { "unlocked" } else { "locked" };

Limitations

The tray does not yet provide hardware-backed unlock, sleep/wake handling on every platform, or a signed installer. Those require platform-specific testing and release work. The tray is a lifecycle companion, not a security boundary by itself.