An Alt+Tab replacement, application launcher, and secret manager for Linux.
Quick Start Β· Features Β· How It Works Β· Usage Β· Configuration Β· Architecture
Open Sesame replaces your Alt+Tab with a window switcher that shows letter hints on every window. Tap Alt+Tab to quick-switch to your previous window, or hold it to see all your windows and type a letter to jump directly. Alt+Shift+Tab cycles backward. Alt+Space opens the full launcher overlay where you can also fuzzy-search and launch applications. All key combos are configurable.
Each window can be bound to a letter in your config. If the app is already open, that letter focuses it. If it's not running, it launches it. Multiple windows of the same app get stacked hints: g, gg, ggg. You configure which apps map to which letters, what command to run if the app isn't open, and optionally which secrets and environment variables to inject at launch time.
Beyond window switching, Open Sesame manages encrypted secret vaults with per-profile trust boundaries, clipboard history with sensitivity detection, keyboard input capture for compositor-independent shortcuts, and text snippet expansion. Secrets can be injected into any command as environment variables (sesame env -p work -- aws s3 ls) or exported in shell, dotenv, or JSON format. Vault unlock supports passwords, SSH agent keys, or both with configurable auth policies.
Everything is scoped to trust profiles. A "work" profile has its own vault, its own secrets, its own clipboard history, its own frecency ranking, and its own launch configurations -- completely separate from "personal" or "default". Profiles activate based on context (WiFi network, connected hardware) or manually.
The system runs as seven cooperating daemons under systemd, communicating over a Noise IK encrypted IPC bus. Each daemon is sandboxed with Landlock filesystem restrictions and seccomp syscall filtering. All key material lives in page-aligned secure memory with guard pages and canary verification -- on Linux 5.14+, secret pages use memfd_secret(2) and are removed from the kernel direct map entirely. The whole thing is controlled through one CLI: sesame.
open-sesame -- headless core
sesameCLI, daemon-profile, daemon-secrets, daemon-launcher, daemon-snippetsRuns anywhere with systemd: desktops, servers, containers, VMs.
open-sesame-desktop -- GUI layer (depends on open-sesame)
daemon-wm, daemon-clipboard, daemon-input
Requires a COSMIC or Wayland desktop.
Install open-sesame-desktop and it pulls in open-sesame automatically. On a server or in a container, install just open-sesame for encrypted secrets and application launching without any GUI dependencies.
Add the GPG key:
curl -fsSL https://scopecreep-zip.github.io/open-sesame/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/open-sesame.gpgAdd the APT repository:
echo "deb [signed-by=/usr/share/keyrings/open-sesame.gpg] https://scopecreep-zip.github.io/open-sesame noble main" | sudo tee /etc/apt/sources.list.d/open-sesame.listInstall on a desktop:
sudo apt update && sudo apt install -y open-sesame open-sesame-desktopOr install headless only (servers, containers, VMs -- no GUI dependencies):
sudo apt update && sudo apt install -y open-sesameAll daemons start automatically after install. Run sesame init to create your config directory, generate IPC keypairs, and set a master password for your first vault:
sesame initsesame statusPress Alt+Tab to switch windows. Press Alt+Space to open the launcher overlay. Configure your key bindings in ~/.config/pds/config.toml (see Configuration below).
|
|
|
Works like traditional Alt+Tab but with letter hints visible for instant selection. Tap -- instantly switch to your previous window. If you release Alt within 250ms (configurable), the overlay never appears and the switch is committed. Hold -- the overlay fades in after 150ms (configurable) showing all windows with hints. Type a letter to jump, or use arrows to navigate. Release Alt to commit the selection. Alt+Shift+Tab -- cycle backward through the MRU stack. |
Opens the full overlay immediately with all windows, letter hints, and a search field. Type a letter to jump to a window. Keep typing to fuzzy-search installed applications. If no window matches, the matched application launches with your configured secrets, env vars, and optionally inside a Nix devshell. Key bindings configured in |
| Key | Action |
|---|---|
| Letter keys | Jump to the window with that hint |
| Arrow keys | Navigate through window list |
| Enter | Activate selected window |
| Escape | Cancel and return to origin window |
| Repeat letter | gg, ggg for multiple windows with the same hint |
| Alt release | Commit the current selection |
All timing parameters (overlay delay, activation delay, quick-switch threshold) are configurable per profile. See the WM configuration section below.
πΉ APT Repository (recommended for Pop!_OS / Ubuntu)
Add the GPG key:
curl -fsSL https://scopecreep-zip.github.io/open-sesame/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/open-sesame.gpgAdd the repository:
echo "deb [signed-by=/usr/share/keyrings/open-sesame.gpg] https://scopecreep-zip.github.io/open-sesame noble main" | sudo tee /etc/apt/sources.list.d/open-sesame.listInstall desktop (full suite):
sudo apt update && sudo apt install -y open-sesame open-sesame-desktopOr install headless only (servers, containers, VMs):
sudo apt update && sudo apt install -y open-sesameInitialize:
sesame initThe open-sesame package installs 5 binaries and systemd user services under open-sesame-headless.target (WantedBy default.target). The open-sesame-desktop package adds 3 GUI daemons under open-sesame-desktop.target (Requires graphical-session.target). Package postinst scripts handle systemctl --global enable and per-user service activation automatically -- no manual systemctl commands needed.
πΉ GitHub Releases (direct download)
Download both packages:
ARCH=$(uname -m)
curl -fsSL "https://github.com/ScopeCreep-zip/open-sesame/releases/latest/download/open-sesame-linux-${ARCH}.deb" -o /tmp/open-sesame.deb
curl -fsSL "https://github.com/ScopeCreep-zip/open-sesame/releases/latest/download/open-sesame-desktop-linux-${ARCH}.deb" -o /tmp/open-sesame-desktop.debVerify build provenance (SLSA):
gh attestation verify /tmp/open-sesame.deb --owner ScopeCreep-zipgh attestation verify /tmp/open-sesame-desktop.deb --owner ScopeCreep-zipInstall (headless first, then desktop):
sudo dpkg -i /tmp/open-sesame.deb /tmp/open-sesame-desktop.debInitialize:
sesame initπΉ Nix Flake (NixOS / home-manager)
Pre-built binaries for x86_64-linux and aarch64-linux are served from scopecreep-zip.cachix.org. The flake includes nixConfig that configures the substituter automatically -- pass --accept-flake-config on first use (or add the cache to your nix.conf):
# Add to your nix.conf or configuration.nix if you prefer not to use --accept-flake-config
nix.settings = {
substituters = [ "https://scopecreep-zip.cachix.org" ];
trusted-public-keys = [ "scopecreep-zip.cachix.org-1:LPiVDsYXJvgljVfZPN43zBWB7ZCGFr2jZ/lBinnPGvU=" ];
};Add the flake input:
# flake.nix
{
inputs.open-sesame = {
url = "github:ScopeCreep-zip/open-sesame";
inputs.nixpkgs.follows = "nixpkgs";
};
}Enable the home-manager module:
# home configuration
{ open-sesame, ... }:
{
imports = [ open-sesame.homeManagerModules.default ];
programs.open-sesame = {
enable = true;
# headless = true; # servers/containers: omit desktop daemons
settings = {
key_bindings.g = {
apps = [ "ghostty" "com.mitchellh.ghostty" ];
launch = "ghostty";
tags = [ "dev" "work:corp" ];
};
key_bindings.f = {
apps = [ "firefox" "org.mozilla.firefox" ];
launch = "firefox";
};
key_bindings.z = {
apps = [ "zed" "dev.zed.Zed" ];
launch = "zed-editor";
};
};
profiles = {
default = {
launch_profiles.dev = {
env = { RUST_LOG = "debug"; };
secrets = [ "github-token" ];
};
};
work = {
launch_profiles.corp = {
env = { CORP_ENV = "production"; };
secrets = [ "corp-api-key" ];
};
};
};
};
}The module generates ~/.config/pds/config.toml, creates systemd user services with dual targets (open-sesame-headless.target always, open-sesame-desktop.target when headless = false), configures SSH_AUTH_SOCK for SSH agent forwarding, and sets up tmpfiles.d rules for runtime directories.
Available Nix packages:
| Package | Description |
|---|---|
packages.open-sesame |
Headless: 5 binaries, no GUI deps (openssl + libseccomp only) |
packages.open-sesame-desktop |
Desktop: 3 GUI daemons + CLI with keybinding commands (propagates headless) |
packages.default |
Desktop (alias) |
πΉ Building from Source
The flake provides a complete dev environment with all native dependencies:
nix developcargo check --workspaceInstall system library headers:
sudo apt-get install -y build-essential pkg-config libssl-dev libseccomp-dev libwayland-dev libxkbcommon-dev libfontconfig1-devcargo check --workspaceMinimum Rust toolchain: see rust-toolchain.toml.
| apt package | Crate(s) | Purpose |
|---|---|---|
libssl-dev |
rusqlite (bundled-sqlcipher) |
OpenSSL for SQLCipher encryption |
libseccomp-dev |
libseccomp |
seccomp-bpf syscall filtering |
libwayland-dev |
wayland-client, smithay-client-toolkit |
Wayland protocol for overlay and clipboard |
libxkbcommon-dev |
xkbcommon |
Keyboard keymap handling |
libfontconfig1-dev |
fontconfig |
Font discovery for overlay rendering |
# First-time setup
sesame init
# With organization namespace
sesame init --org braincraft.io
# SSH-only vault
sesame init --ssh-key
# Dual-factor vault (password + SSH)
sesame init --ssh-key --password
# Dual-factor, all factors required
sesame init --ssh-key --password --auth-policy all
# Specific SSH key
sesame init --ssh-key ~/.ssh/id_ed25519.pub
# Factory reset
sesame init --wipe-reset-destroy-all-data
# Check status
sesame status# Unlock (auto-tries SSH agent, then password)
sesame unlock
sesame unlock -p work
sesame unlock -p "default,work"
# Lock (zeroizes cached key material)
sesame lock
sesame lock -p work# Enroll (interactive selection from agent)
sesame ssh enroll -p default
# Enroll specific key
sesame ssh enroll -p work -k SHA256:abc123...
sesame ssh enroll -p work -k ~/.ssh/id_ed25519.pub
# List and revoke
sesame ssh list
sesame ssh revoke -p workAfter enrollment, unlock and the window switcher overlay attempt SSH agent unlock automatically before falling back to password. # Store (prompts for value)
sesame secret set -p default github-token
# Retrieve
sesame secret get -p default github-token
# List keys (never shows values)
sesame secret list -p work
# Delete
sesame secret delete -p work old-key
sesame secret delete -p work old-key --yes |
# Run with secrets as env vars
sesame env -p work -- aws s3 ls
# With prefix (api-key -> MYAPP_API_KEY)
sesame env -p work --prefix MYAPP -- ./start.sh
# Multi-profile
sesame env -p "default,work" -- make deployA runtime denylist blocks injection of # Shell eval (bash/zsh/direnv)
eval "$(sesame export -p work)"
# Dotenv (Docker/node/python-dotenv)
sesame export -p work -f dotenv > .env.secrets
# JSON (jq/CI/CD)
sesame export -p work -f json | jq .
# With prefix
sesame export -p work --prefix MYAPP -f shellsesame wm overlay
sesame wm overlay --launcher
sesame wm overlay --backward
sesame wm switch
sesame wm switch --backward
sesame wm focus firefox
sesame wm listsesame launch search firefox
sesame launch search "visual studio" -n 5
sesame launch run org.mozilla.firefox
sesame launch run org.mozilla.firefox -p worksesame profile list
sesame profile show work
sesame profile activate work
sesame profile deactivate work
sesame profile default worksesame workspace init
sesame workspace clone https://github.com/org/repo
sesame workspace clone git@github.com:org/repo --depth 1
sesame workspace link -p work
sesame workspace list
sesame workspace status
sesame workspace shell
sesame workspace shell -p work -- make build
sesame workspace config show
sesame workspace unlinksesame clipboard history -p default -n 50
sesame clipboard clear -p work
sesame clipboard get <entry-id>
sesame input layers
sesame input status
sesame snippet list -p default
sesame snippet add -p default "@@sig" "Best,\nJohn"
sesame snippet expand -p default "@@sig"
sesame audit verify
sesame audit tail -n 50 -fsesame setup-keybinding
sesame setup-keybinding super+space
sesame keybinding-status
sesame remove-keybinding |
Open Sesame uses ~/.config/pds/config.toml with layered inheritance:
/etc/pds/policy.toml # System policy (enterprise, read-only)
~/.config/pds/config.toml # User config
~/.config/pds/config.d/*.toml # Drop-in fragments (alphabetical)
~/.config/pds/workspaces.toml # Workspace-to-profile links
~/.config/pds/installation.toml # Installation identity (generated by sesame init)
Data locations:
~/.config/pds/vaults/ # Encrypted SQLCipher vault databases
~/.config/pds/keys/ # Noise IK daemon keypairs
~/.config/pds/enrollments/ # SSH key enrollment blobs
~/.config/pds/audit.jsonl # Hash-chained audit log
$XDG_RUNTIME_DIR/pds/ # Runtime: IPC socket, bus public key
π Full example configuration
config_version = 3
[global]
default_profile = "default"
[global.ipc]
# channel_capacity = 1024
# slow_subscriber_timeout_ms = 5000
[global.logging]
level = "info"
# json = false
# journald = true
# ββ Profile: default ββββββββββββββββββββββββββββββββββββββββββββββ
[profiles.default]
name = "default"
# Authentication: how vault unlock factors combine
# "any" β any single enrolled factor unlocks (password OR ssh-agent)
# "all" β all enrolled factors required (BLAKE3 combined key)
# "policy" β required factors + threshold of additional enrolled factors
[profiles.default.auth]
mode = "any"
# required = ["password", "ssh-agent"] # for mode = "policy"
# additional_required = 0 # for mode = "policy"
# Window Manager settings
[profiles.default.wm]
hint_keys = "asdfghjkl"
overlay_delay_ms = 150 # ms before full overlay appears
activation_delay_ms = 200 # ms delay before committing a hint match
quick_switch_threshold_ms = 250 # Alt+Tab released within this = instant switch
border_width = 4.0
border_color = "#89b4fa"
background_color = "#000000c8"
card_color = "#1e1e1ef0"
text_color = "#ffffff"
hint_color = "#646464"
hint_matched_color = "#4caf50"
show_title = true
show_app_id = false
max_visible_windows = 20
# ββ Key Bindings ββββββββββββββββββββββββββββββββββββββββββββββββββ
# Each section maps a letter to app IDs and an optional launch command.
# Multiple windows of the same app get repeated keys: g, gg, ggg
# Find your app_ids: sesame wm list
# Terminals
[profiles.default.wm.key_bindings.g]
apps = ["ghostty", "com.mitchellh.ghostty"]
launch = "ghostty"
# Browsers
[profiles.default.wm.key_bindings.f]
apps = ["firefox", "org.mozilla.firefox", "Firefox"]
launch = "firefox"
[profiles.default.wm.key_bindings.e]
apps = ["microsoft-edge", "com.microsoft.Edge", "Microsoft-edge"]
launch = "microsoft-edge"
[profiles.default.wm.key_bindings.v]
apps = ["vivaldi", "vivaldi-stable"]
launch = "vivaldi"
[profiles.default.wm.key_bindings.c]
apps = ["chromium", "google-chrome", "Chromium", "Google-chrome"]
# No launch β focus only
# Editors
[profiles.default.wm.key_bindings.z]
apps = ["zed", "dev.zed.Zed"]
launch = "zed-editor"
# File Managers
[profiles.default.wm.key_bindings.n]
apps = ["nautilus", "org.gnome.Nautilus", "com.system76.CosmicFiles"]
launch = "nautilus"
# Communication
[profiles.default.wm.key_bindings.s]
apps = ["slack", "Slack"]
launch = "slack"
[profiles.default.wm.key_bindings.d]
apps = ["discord", "Discord"]
launch = "discord"
[profiles.default.wm.key_bindings.t]
apps = ["thunderbird", "Thunderbird"]
launch = "thunderbird"
# Media
[profiles.default.wm.key_bindings.m]
apps = ["spotify", "Spotify"]
launch = "spotify"
# ββ Launch Profiles βββββββββββββββββββββββββββββββββββββββββββββββ
# Named, composable environment bundles applied via the `tags` field on
# key bindings. Tags support cross-profile references: "work:corp" resolves
# the "corp" launch profile in the "work" trust profile.
#
# When multiple tags are applied, env vars merge (later tag wins on conflict),
# secrets accumulate, and last devshell/cwd wins.
[profiles.default.launch_profiles.dev]
env = { RUST_LOG = "debug" }
secrets = ["github-token"]
# devshell = "/workspace/myproject#rust"
# cwd = "/workspace/usrbinkat/github.com/org/repo"
# Launcher settings
[profiles.default.launcher]
max_results = 20
frecency = true
# Clipboard settings
[profiles.default.clipboard]
max_history = 1000
sensitive_ttl_s = 30
detect_sensitive = true
# Audit settings
[profiles.default.audit]
enabled = true
retention_days = 90
# ββ Profile: work (example) ββββββββββββββββββββββββββββββββββββββ
# [profiles.work]
# name = "work"
#
# [profiles.work.auth]
# mode = "all"
#
# [profiles.work.launch_profiles.corp]
# env = { CORP_ENV = "production" }
# secrets = ["corp-api-key", "corp-signing-key"]
# cwd = "/workspace/usrbinkat/github.com/acme-corp"
#
# # Tag a key binding with a cross-profile launch profile:
# # [profiles.default.wm.key_bindings.g]
# # apps = ["ghostty"]
# # launch = "ghostty"
# # tags = ["dev", "work:corp"]
# #
# # This composes: "dev" from default + "corp" from work profile.
# # Environment merges, secrets accumulate, last devshell/cwd wins.
# ββ Cryptographic Algorithms ββββββββββββββββββββββββββββββββββββββ
[crypto]
kdf = "argon2id" # or "pbkdf2-sha256"
hkdf = "blake3" # or "hkdf-sha256"
noise_cipher = "chacha-poly" # or "aes-gcm"
noise_hash = "blake2s" # or "sha256"
audit_hash = "blake3" # or "sha256"
minimum_peer_profile = "leading-edge" # or "governance-compatible", "custom"π Advanced: launch profiles with cross-profile tags
Launch profiles let you compose environment variables, secrets, and Nix devshells from multiple trust boundaries at launch time:
# Tag a key binding with launch profiles from multiple trust boundaries
[profiles.default.wm.key_bindings.g]
apps = ["ghostty"]
launch = "ghostty"
tags = ["dev-rust", "work:corp"]
launch_args = ["--working-directory=/workspace/user/github.com/org/repo"]
[profiles.default.launch_profiles.dev-rust]
env = { RUST_LOG = "debug", CARGO_HOME = "/workspace/.cargo" }
secrets = ["github-token", "crates-io-token"]
devshell = "/workspace/myproject#rust"
cwd = "/workspace/user/github.com/org/repo"
# "work:corp" references the "corp" launch profile in the "work" trust profile.
# When multiple tags are applied:
# - env vars merge (later tag wins on conflict)
# - secrets accumulate (union of all tags)
# - last devshell/cwd winsπ Per-directory configuration (.sesame.toml)
Place a .sesame.toml in any workspace or repo root for directory-scoped defaults:
# Default profile when working in this directory
profile = "work"
# Environment variables (non-secret)
[env]
RUST_LOG = "debug"
PROJECT_NAME = "my-project"
# Launch profile tags applied automatically
tags = ["dev-rust"]
# Prefix for secret injection
secret_prefix = "MYAPP"ποΈ Workspace configuration (~/.config/pds/workspaces.toml)
[settings]
root = "/workspace"
default_ssh = true
[links]
"/workspace/usrbinkat/github.com/corp" = "work"
"/workspace/usrbinkat/github.com/personal" = "default"Run the built-in system health check:
sesame status --doctorThis checks all 7 daemons (running, memory, restarts, uptime), memory protection (memfd_secret, core dumps, swap), sandbox (seccomp, NoNewPrivs), and platform (kernel version, ptrace scope, Wayland session). Run specific categories with sesame status --doctor=daemon or sesame status --doctor=memory,sandbox.
When filing a bug report, include the full doctor output:
sesame status --doctor --output jsonDaemons not running
sesame statussystemctl --user list-units "open-sesame-*"journalctl --user -u open-sesame-profile -fjournalctl --user -u open-sesame-wm -fjournalctl --user -u open-sesame-secrets -fNo windows in overlay
sesame wm listRequires COSMIC or a Wayland compositor with ext-foreign-toplevel protocol support. X11 is not supported.
Wrong app IDs in config
sesame wm listCopy the exact app_id shown and use it in your key_bindings configuration.
Keybinding not working
sesame keybinding-statussesame remove-keybinding && sesame setup-keybindingCheck for conflicts with other COSMIC shortcuts.
SSH agent unlock not working
ssh-add -lsesame ssh list -p defaultIf no enrollment exists, unlock with password first then enroll:
sesame unlock -p defaultsesame ssh enroll -p defaultFor SSH agent forwarding on remote VMs, ensure SSH_AUTH_SOCK is available to systemd user services. The home-manager module handles this via ~/.ssh/agent.sock stable symlink.
Input daemon requires input group
daemon-input needs /dev/input/* access for keyboard capture when no window is focused:
sudo usermod -aG input $USERLogout and login required.
Debug logging
systemctl --user set-environment RUST_LOG=debugsystemctl --user restart open-sesame-headless.targetsystemctl --user restart open-sesame-desktop.targetjournalctl --user -u open-sesame-profile -fOr run a single daemon manually:
systemctl --user stop open-sesame-wmRUST_LOG=debug daemon-wmMemory lock limit exhausted (mlock ENOMEM)
memfd_secret pages are implicitly locked by the kernel and count against RLIMIT_MEMLOCK. If daemons fail with mlock errors, raise the limit:
# Check current limit
ulimit -lThe systemd units set LimitMEMLOCK=64M. If running daemons manually:
ulimit -l 65536 && daemon-secretsFor system-wide configuration:
# /etc/security/limits.d/open-sesame.conf
* soft memlock 65536
* hard memlock 65536memfd_secret not available (ERROR in daemon logs)
If daemon startup logs show memfd_secret unavailable, the fallback to mmap(MAP_ANONYMOUS) is active. Secrets are functional but remain on the kernel direct map.
# Check kernel support
grep SECRETMEM /boot/config-$(uname -r)Expected: CONFIG_SECRETMEM=y. If CONFIG_SECRETMEM is not set or equals n, your kernel was built without it. This is common on older distributions and some cloud provider kernels. Upgrade to a kernel with CONFIG_SECRETMEM=y for full security posture.
Overlay feels slow
Reduce delays in your config:
[profiles.default.wm]
overlay_delay_ms = 0 # Show immediately
activation_delay_ms = 100 # Faster activation (may skip gg/ggg disambiguation)Open Sesame is 21 Rust crates organized as seven daemons, eight core libraries, and six platform/tooling crates, communicating over a Noise IK encrypted IPC bus.
graph TB
subgraph CLI
S["sesame"]
end
subgraph "Headless Daemons"
P["daemon-profile<br>IPC Bus Host"]
SEC["daemon-secrets<br>Encrypted Vaults"]
L["daemon-launcher<br>App Launcher"]
SN["daemon-snippets<br>Text Expansion"]
end
subgraph "Desktop Daemons"
WM["daemon-wm<br>Window Overlay"]
CB["daemon-clipboard<br>Clipboard"]
IN["daemon-input<br>Keyboard"]
end
subgraph "Core Libraries"
IPC["core-ipc<br>Noise IK Transport"]
CRYPTO["core-crypto<br>Argon2id, BLAKE3, AES-GCM"]
MEM["core-memory<br>memfd_secret, Guard Pages"]
AUTH["core-auth<br>Password + SSH Agent"]
TYPES["core-types<br>Protocol Schema"]
end
S -->|"ephemeral connection"| P
SEC --> P
L --> P
SN --> P
WM --> P
CB --> P
IN --> P
P --- IPC
SEC --- CRYPTO
CRYPTO --- MEM
TYPES --- MEM
SEC --- AUTH
P --- TYPES
style P fill:#4a9eff
style SEC fill:#ff6b6b
style WM fill:#50c878
style S fill:#ffa500
style MEM fill:#9b59b6
All daemons communicate through daemon-profile which hosts a Noise IK encrypted bus on $XDG_RUNTIME_DIR/pds/bus.sock. Every connection is authenticated with X25519 key exchange and ChaChaPoly AEAD. Peer identity is bound via kernel UCred (PID, UID) in the Noise prologue. Messages are postcard-encoded with security level enforcement -- a daemon can only send messages at or below its registered clearance level.
The two targets compose cleanly:
| Target | WantedBy | Daemons |
|---|---|---|
open-sesame-headless.target |
default.target |
profile, secrets, launcher, snippets |
open-sesame-desktop.target |
graphical-session.target |
wm, clipboard, input |
The desktop target Requires the headless target. Starting desktop starts headless first. Stopping desktop leaves headless running. All daemons use Type=notify with WatchdogSec=30 for health monitoring.
Password --> Argon2id(password, per-profile-salt) --> Master Key (32 bytes, ProtectedAlloc)
|
+--> BLAKE3 derive_key("pds v2 vault-key {profile}") --> SQLCipher page key
+--> BLAKE3 derive_key("pds v2 clipboard-key {profile}") --> Clipboard encryption key
+--> BLAKE3 derive_key("pds v2 ipc-auth-token {profile}") --> IPC auth token
+--> BLAKE3 derive_key("pds v2 ipc-encryption-key {profile}") --> IPC field encryption key
SSH Agent (alternative/additional factor):
SSH sign(challenge) --> BLAKE3 derive_key("pds v2 ssh-vault-kek {profile}") --> KEK
KEK wraps Master Key via AES-256-GCM --> EnrollmentBlob on disk
All-mode (both factors required):
BLAKE3 derive_key("pds v2 combined-master-key {profile}", sorted_factor_pieces)
--> Combined Key
π Core libraries and platform crates
| Crate | Purpose |
|---|---|
core-memory |
Page-aligned secure allocator: ProtectedAlloc with guard pages, canary, memfd_secret(2) backend, volatile zeroize. All secret-carrying types are backed by this |
core-ipc |
Noise IK transport (X25519 + ChaChaPoly + BLAKE2s), BusServer/BusClient, clearance registry, postcard framing, UCred binding |
core-types |
Canonical type system: EventKind protocol schema, DaemonId, SecurityLevel, TrustProfileName, SensitiveBytes with ProtectedAlloc backing and custom serde Visitor |
core-crypto |
Argon2id KDF, BLAKE3 HKDF, AES-256-GCM encryption, SecureBytes/SecureVec with ProtectedAlloc backing and zero-copy transfer |
core-config |
TOML configuration schema with XDG layered inheritance, inotify hot-reload watcher, config validation |
core-auth |
Multi-factor authentication: PasswordBackend (Argon2id KEK wrapping), SshAgentBackend (deterministic signature KEK), AuthDispatcher, VaultMetadata persistence |
core-secrets |
SQLCipher database abstraction, KeyLocker trait, JIT secret cache |
core-profile |
Profile context evaluation, hash-chained BLAKE3 audit logger |
core-fuzzy |
Nucleo fuzzy matching engine with frecency scoring backed by SQLite |
platform-linux |
Wayland/COSMIC compositor backends (CosmicBackend, WlrBackend), Landlock sandbox, seccomp-bpf, D-Bus (SSID monitor, Secret Service), COSMIC key injection, systemd notify |
sesame-workspace |
Workspace discovery, canonical path convention, git operations, platform-specific root resolution |
extension-host |
WASI extension runtime (Wasmtime + component model) |
extension-sdk |
Extension development SDK (WIT bindings) |
platform-macos |
macOS platform abstractions (scaffolded: accessibility, keychain, launch agents) |
platform-windows |
Windows platform abstractions (scaffolded: credential vault, hotkeys, UI automation) |
π‘οΈ Security model
- Noise IK encrypted IPC -- All inter-daemon communication authenticated and encrypted with forward secrecy
- SQLCipher encrypted vaults -- AES-256-CBC with HMAC-SHA512 per page, Argon2id key derivation (19 MiB memory, 2 iterations)
- SSH agent unlock -- Master key wrapped under KEK derived from deterministic SSH signatures (Ed25519/RSA PKCS#1 v1.5)
- Page-aligned secure memory (
core-memory) -- All secret-carrying types (SecureBytes,SecureVec,SensitiveBytes) backed byProtectedAlloc: guard pages (PROT_NONE) on both sides of every allocation, 16-byte random canary with constant-time verification on drop, user data right-aligned so overflows hit the trailing guard page (SIGSEGV), volatile zeroize before munmap. On Linux 5.14+ withCONFIG_SECRETMEM=y, allocations usememfd_secret(2)-- pages are removed from the kernel direct map and invisible to/proc/pid/mem, kernel modules, DMA, and ptrace even as root. Older kernels fall back tommap(MAP_ANONYMOUS)withmlock(2)+MADV_DONTDUMP, logged at ERROR with compliance impact statement - Zero-copy secret lifecycle -- Derived keys go directly from stack arrays into ProtectedAlloc via
from_slice(). SecureBytes transfers to SensitiveBytes viainto_protected_alloc()with zero heap intermediaries. Custom serdeVisitordeserializes directly from postcard's borrowed input buffer into ProtectedAlloc. Secret bytes never touch the unprotected heap during normal operation - Landlock filesystem sandboxing -- Per-daemon path-based access control. Daemons can only access their specific runtime directories. Partially enforced Landlock is treated as a fatal error -- no degradation
- seccomp-bpf syscall filtering -- Per-daemon allowlists. Unallowed syscalls terminate the offending thread (
SECCOMP_RET_KILL_THREAD) with a SIGSYS handler for visibility - Hash-chained audit log -- BLAKE3 hash chain provides tamper evidence for all vault operations.
sesame audit verifydetects modifications, deletions, and reorderings - Rate limiting -- Vault unlock attempts throttled via governor token bucket
- systemd hardening --
NoNewPrivileges,ProtectSystem=strict,ProtectHome=read-only,PrivateNetwork(secrets daemon),LimitCORE=0, memory limits, capability bounding - Environment injection denylist -- Blocks
LD_PRELOAD,BASH_ENV,NODE_OPTIONS,PYTHONSTARTUP,JAVA_TOOL_OPTIONS, and 30+ other injection vectors across all major runtimes - Explicit security posture -- Security controls that fail are fatal. The one exception is
memfd_secretavailability: systems without it fall back tommapwith an ERROR-level audit log naming the compliance frameworks not met (IL5/IL6, STIG, PCI-DSS) and the exact remediation command. Silent degradation does not exist
π§ Design principles
- Multi-daemon isolation -- Each concern in its own process with minimal privileges and tailored sandbox
- Profile-scoped everything -- Secrets, clipboard, frecency, snippets, audit all scoped to trust profiles
- Fast activation -- Sub-200ms window switching with staged commit model
- Headless-first -- Every CLI command works from explicit primitives without interactive prompts
- Composable configuration -- System policy β user config β drop-ins β profile overrides β workspace overrides
- Deterministic security -- No race conditions in lock/unlock. No "should never happen" code paths
|
|
For development: nix develop (recommended) or system packages listed in Building from Source. Task runner: mise with 100+ tasks in .mise.toml.
Contributions are welcome. The test suite allocates memfd_secret pages which count against RLIMIT_MEMLOCK. Raise the limit before running tests:
sudo prlimit --pid $$ --memlock=268435456:268435456Or use the Nix devshell which handles this automatically:
nix developRun the quality gates before submitting:
cargo fmt --checkcargo clippy --workspace --all-targetscargo test --workspacecargo build --workspaceVerify system health after changes:
sesame status --doctorFor larger contributions, open an issue first to discuss the approach.
Open Sesame is licensed under the GNU General Public License v3.0.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Built with Rust, smithay-client-toolkit, COSMIC Protocols, snow, SQLCipher, tiny-skia, cosmic-text, nucleo, argon2, blake3, and aes-gcm.
Inspired by Vimium -- the browser extension that proves keyboard navigation is superior.
