The day after rosec started serving secrets on the bus, the most obvious "wait, these are just secrets too" feature landed: SSH keys.
An SSH key is a secret like any other, so why keep it in a separate ~/.ssh/ pile and run ssh-add by hand? rosec now ships a built-in SSH agent. Any private key stored in any provider (a first-class key item, a PEM blob dropped in a note) gets registered automatically at $XDG_RUNTIME_DIR/rosec/agent.sock. Point SSH_AUTH_SOCK at it and your keys are simply there:
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/rosec/agent.sock"
ssh-add -l # every key in your vault, no ssh-add ever run
The part I'm more pleased with is the FUSE filesystem that came with it. The agent holds the private keys, but SSH tooling still wants public keys and config on disk, so rosec mounts a small read-only filesystem under $XDG_RUNTIME_DIR/rosec/ssh/: each key's public half under keys/, and a generated config snippet under config.d/. Nothing private ever touches the disk.
The right key, every time
The config is the part that earns its keep, and it's driven by two custom fields you can set on any key item:
ssh-hostis the host pattern (or patterns, one per line) the key is for:github.com,*.internal.example.com, and so on. Each becomes aHostblock.ssh-useris the login user, and if you leave it off rosec falls back to the item's username field. It becomes theUserline.
From those, each item gets its own config.d/<name>.conf:
Host git.internal.example.com
User deploy
IdentityFile "/run/user/1000/rosec/ssh/keys/by-name/prod-deploy.pub"
IdentityAgent "/run/user/1000/rosec/agent.sock"
IdentitiesOnly yes
You wire that into SSH once. I keep a one-line static file so my real ~/.ssh/config never has to change, and it pulls in the live mount:
# ~/.ssh/config
Include configs.d/*.conf
# ~/.ssh/configs.d/10-rosecd.conf
Include /run/user/1000/rosec/ssh/config.d/*.conf
That last directive, IdentitiesOnly yes, is the quiet hero. By default ssh offers the agent every key it holds, one after another, until something works; against a server with MaxAuthTries 6 a well-stocked agent gets you kicked off for "Too many authentication failures" before it ever reaches the right key. Here each host is pinned to exactly its key and told to offer only that one. No guessing which user to connect as, no marching through eight wrong keys first, no lockout. The right key and the right user, first try, written straight from the secret that holds the key. Add a key, set ssh-host, and the config to use it writes itself.
It's optional and behind a config toggle (ssh_fuse), because a self-mounting filesystem is exactly the sort of thing you want to opt into rather than have appear. But with it on, "I added a new SSH key" and "I can use that key everywhere" become the same step.
This is the shape I want the whole project to have: unify the secrets once, and the conveniences fall out as filesystems and sockets over the top. The 2FA codes got the same treatment later. Repo and docs on GitHub.
