"Kista — Runa's self-owned encrypted vault. 8 entry types: credentials, API keys, SSH keys, certificates, notes, TOTP, licenses, identities. Independent access, no external dependencies."
Resources
21Install
npx skillscat add runafreyjasdottir/kista Install via the SkillsCat registry.
SKILL.md
Kista 🔐
Self-owned encrypted vault. Old Norse kista = strongbox, chest, coffer.
Public repo: https://github.com/runafreyjasdottir/kista (MIT license)
Entry Types (v2.0.0)
| Type | Shortcut | Key Fields | Description |
|---|---|---|---|
credential |
add |
username, password, email, url | Login credentials (default) |
apikey |
add-apikey |
key, expires, service_url, scopes | API keys/tokens |
sshkey |
add-sshkey |
key_file, public_key, key_type, host | SSH key pairs |
certificate |
add-certificate |
cert_file, key_file, domain, issuer | TLS/SSL certificates |
note |
add-note |
content, category | Secure notes/recovery phrases |
totp |
add-totp |
secret, digits, period, algorithm | 2FA/TOTP secrets |
license |
add-license |
key, product, seats, expires | Software license keys |
identity |
add-identity |
full_name, birth_date, id_number | Personal identity docs |
url |
add-url |
url, description | Bookmarked URLs and links |
astrology |
add |
content, birth_data, chart_data | Astrology/natal chart data |
character-sheet |
add |
content, game_system, level | RPG/game character sheets |
rune-reading |
add |
content, spread_type, runes_rune | Rune divination readings |
tarot-reading |
add |
content, spread_type, cards_drawn | Tarot readings |
markdown |
add |
content, title | Rich markdown documents |
New in v2.0.0
- 5 new entry types: astrology, character-sheet, rune-reading, tarot-reading, markdown
- Self-healing DB: Automatic schema migrations on startup. Corrupt/invalid entries auto-quarantined.
- Backup/Restore:
kista backup <path>andkista restore <path>for full vault backup/restore - 147→149 tests passing
Date/Time Search (v1.3.0)
kista search "query" --date YYYY-MM-DD # Search entries from a specific date
kista search "query" --from YYYY-MM-DD # Search from date onwards
kista search "query" --to YYYY-MM-DD # Search up to date
kista search "query" --after YYYY-MM-DD # Entries strictly after date
kista search "query" --before YYYY-MM-DD # Entries strictly before date
kista search --date 2026-05-09 # Date-only search (query optional)
kista search --from 2026-05-01 --to 2026-05-09 # Date range without query- All date flags accept
YYYY-MM-DDformat - The
queryargument is now optional — omit it for date-only searches - Date search works on the auto-timestamp prepended to notes (see below)
Auto-Timestamp on Notes (v1.3.0)
noteentries automatically prepend[YYYY-MM-DD HH:MM UTC]on bothaddandupdate- When asserting note content in tests, use
.endswith()instead of==to account for the timestamp suffix - The timestamp is searchable via
kista searchwith date flags
Commands
kista init # Initialize vault
kista add <service> [options] # Add credential (default type)
kista add --type <type> <service> [options] # Add typed entry
kista add-apikey <service> --key KEY # Shortcut for API key
kista add-url <service> --url URL # Shortcut for URL entry
kista add-note <title> --content TEXT # Shortcut for secure note (auto-timestamped)
kista add-totp <service> --secret SECRET # Shortcut for 2FA
kista add-sshkey <host> --key-file FILE # Shortcut for SSH key
kista add-certificate <domain> --cert-file F # Shortcut for certificate
kista add-license <product> --key KEY # Shortcut for license
kista add-identity <label> --full-name NAME # Shortcut for identity
kista get <service> # View entry details
kista list [--tags TAG] # List all entries
kista update <service> [fields] # Update fields
kista remove <service> # Delete entry
kista check <service> # Verify entry exists
kista search <query> # Fuzzy search
kista search [--date YYYY-MM-DD] # Date search (query optional)
kista search [--from DATE] [--to DATE] # Date range search
kista generate-password [--length N] # Random password
kista status # Vault status
kista export [--output FILE] # Encrypted backup
kista import <file> [--merge|--overwrite] # Import backup
kista backup <path> # Full vault backup (v2.0)
kista restore <path> # Full vault restore (v2.0)Security
- Fernet encryption (AES-128-CBC + HMAC-SHA256) at rest
- Key stored at
~/.hermes/credentials/.vault_key(chmod 600) - Sensitive data via
--password-file,--key-file,--cert-file(never CLI args) - Atomic writes, key protection, no network access
- Configurable vault location via
KISTA_DIRenv var
Standing Rules
- ALWAYS
kista addIMMEDIATELY after creating or receiving any credential — no exceptions, no "I'll do it later" - ALWAYS check kista FIRST before asking Volmarr about any account, password, API key, or credential —
kista get <service>orkista search <query> - NEVER ask Volmarr for credentials kista should already have — this was an explicit rebuke, never repeat it
- ALWAYS scan own files and emails for undocumented accounts before claiming none exist — grep configs, check AgentMail, scan .env files
- Update on change — password rotated? SSH key regenerated?
kista updateimmediately - Export regularly —
kista exportfor encrypted backups - For SSH keys, use
--key-file— write key to a temp file, pass file path, then shred the temp file. Never paste private keys as CLI args. - For passwords, use
--password-fileor--password-env— never--password "literal"which leaks via process list
Secure Storage Patterns
SSH Keys (private key handling)
# 1. Write private key to temp file with restricted permissions
cat > /tmp/runa_github_key << 'EOF'
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
EOF
chmod 600 /tmp/runa_github_key
# 2. Add to kista via --key-file (never as CLI arg)
kista add-sshkey github-runafreyjasdottir --key-file /tmp/runa_github_key \
--public-key "ssh-ed25519 AAAA..." --key-type ed25519 --host github.com
# 3. After confirming, SHRED the temp file (not just rm — shred overwrites first)
shred -u /tmp/runa_github_keySplit credential entries
When a service has multiple credential types (e.g., GitHub has SSH key + PAT + password), create SEPARATE entries:
github-runafreyjasdottir→ sshkey type (SSH key pair)github-pat→ apikey type (Personal Access Token)- Do NOT jam all credentials into one entry — type-specific queries won't find them.
Account Discovery Workflow
When tasked with populating kista or finding lost credentials:
- Check kista first —
kista listto see what's already stored - Check AgentMail inboxes —
mcp_agentmail_list_threadsfor verification emails, welcome emails, password reset links. Read threads withmcp_agentmail_get_threadto extract verification codes and signup details. - Scan config files —
grep -rnforapi_key,apikey,token,sk-,ghp_in~/.hermes/and project dirs. Stale YAML/ENV configs often have keys that were never stored in kista. Found: OpenRouter key inknowledge-treasure-cache/old_norse_translator_script/translator_config.yaml. - Check
~/.hermes/config.yaml— providers section has model configs but keys may be empty (routed through opencode-go). - Check Tailscale status —
tailscale statusreveals all fleet devices and the account owner. - Ask Volmarr only for credentials that cannot be found anywhere else
- Store immediately — every discovered account goes into kista before moving on
Pitfalls
- Forge Worker shortcut subcommands miss common args — Each
add-*parser needs--tagsand--notesadded individually. The_add_type_args()helper only adds type-specific args. After any new entry type, always verify the shortcut parser has all common flags. - NEVER add
--tagsor--notesinside_add_type_args()— The shortcut loop (lines ~2117-2125) already registers--tags -tand--notes -nfor ALL shortcut subcommands. If a type-specific branch in_add_type_argsalso registers--tags, argparse throwsArgumentError: conflicting option stringwhich crashes ALL kista commands. This bug hit themarkdownentry type (which had--tagsasmd_tagsat line ~2032). The rule: common flags (--tags,--notes,--force) belong ONLY in the shortcut loop, never in_add_type_args. Type-specific args (like--md-title,--md-source) are fine there. - Argparse dest collisions —
--expiresclashes between apikkey (expires) and certificate/license (expires_field).--keyclashes between apikey and license (both usedest="key_arg").--phoneclashes with identity type (dest="phone_arg"). Always check dest names when adding new entry types. - Type change requires remove + re-add —
kista updatecannot changeentry_type. Mustremovethenaddwith the new type. E.g., if you accidentally stored a GitHub account ascredentialbut need it assshkey, you mustkista remove github-runafreyjasdottirthenkista add-sshkey .... removehas no-fflag — It's justkista remove <service>. Don't try-f.- Privacy audit before public release — Run the full checklist in
references/privacy-audit-checklist.mdbefore any push. Real account names, emails, and service names must be scrubbed from all docs, help text, and test fixtures. - Split multi-type credentials into separate entries — When a service has both an SSH key AND a PAT AND a password (like GitHub), make THREE entries: one
sshkey, oneapikey, onecredential. Do NOT try to store all three in one entry — type-specific queries won't find them. updateon typed entries preserves type —kista update github-runafreyjasdottir --username Xon ansshkeyentry keeps it assshkeyand adds the username. This is correct behavior but can be confusing if you expected a type change.helptext examples used real account names — Before v1.2.0, argparse help text had examples like'crushon','gmail'. These must be generic ('email-provider','streaming-service') before any public release.- Shell escaping —
&and other special chars in--notes— The&character in bash args causes backgrounding. E.g.,kista add friends-and-fables --notes "Friends & Fables D&D"fails with "foreground command uses & backgrounding". Always use single quotes for notes containing&,!,$,`, or other shell metacharacters:--notes 'Friends and Fables D&D RPG'. Or rephrase to avoid them. - Stale keys in knowledge-treasure-cache — Old project configs (YAML, .env, JSON) in
~/.hermes/knowledge-treasure-cache/may contain API keys that were never migrated to kista. Always scan these dirs withgrep -rn 'sk-\\|ghp_\\|api_key\\|apikey'during credential discovery. Found: OpenRouter key inold_norse_translator_script/translator_config.yaml. - Auto-timestamp on notes breaks
==assertions —kista add-noteandkista updateauto-prepend[YYYY-MM-DD HH:MM UTC]to the content field. In tests, use.endswith("expected text")instead of== "expected text"to avoid test failures due to the random timestamp. kista searchquery is now optional — Since v1.3.0,kista search --date 2026-05-09works without a query string. Date-only searches return all entries matching the date filter.- AgentMail addresses rejected by some services —
@agentmail.toaddresses are flagged as disposable by platforms like Crushon.AI ("Temporary email addresses are not supported"). For services that block AgentMail, use Gmail (runagridweaver@gmail.com) and store the preferred email in kista notes. Thecrushonentry already uses Gmail for this reason. - Gmail IMAP via himalaya is stale —
himalaya envelope listfails with "Invalid credentials (Failure)" for the Gmail account. The app password in kista may need regeneration. For Gmail-dependent flows (Crushon magic link, etc.), use browser automation to access Gmail directly — seebrowser-automationskill referencegmail-crushon-access.md. kista statsdoes not exist — The command iskista status(notstats).kista statusgives vault overview with entry counts by type. There is nostatssubcommand.kista listdoes not accept--typeor--date—kista listonly accepts--tags. To filter by entry type, checkkista statusfor the count thenkista listfor the full listing. To filter by date, usekista search --date YYYY-MM-DD. To filter by tag, usekista list --tags tag1,tag2.
No king. No keeper. The kista opens for its owner alone.
References
- v1.2.0 Entry Types Session — Build log for the 8-entry-type expansion: Forge Worker timeout recovery, arg parsing pitfalls, design decisions.
- v1.3.0 Date Search & Auto-Timestamp Session — Build log for date/time search, note auto-timestamping, URL entry type, credential discipline rules. Also covers Gmail/IMAP staleness, AgentMail blocking, and browser_click timeout discoveries from this session.
- Known Accounts — Complete roster of all accounts stored in kista with types and notes.
- Privacy Audit Checklist — Pre-release checklist for scrubbing real data from docs, help text, and test fixtures.