Credential Vault
Securely store credentials with AES-256-GCM encryption, automate logins, generate TOTP codes, and audit vault access
Overview
The Wraith credential vault is an AES-256-GCM encrypted store backed by a local SQLite database at ~/.wraith/vault.db. Every secret is encrypted at rest. The vault supports passwords, API keys, OAuth tokens, TOTP seeds, session cookies, and generic secrets.
The vault integrates directly with browse_login for automated authentication flows and browse_vault_totp for generating 2FA codes. All access is logged to an audit trail.
How encryption works
The vault uses AES-256-GCM (Galois/Counter Mode), an authenticated encryption scheme that provides both confidentiality and integrity:
- Key derivation: The master key is derived from your passphrase (or an empty passphrase in MCP auto-unlock mode)
- Per-credential encryption: Each credential gets a unique nonce. The secret value is encrypted; metadata (domain, kind, identity) is stored in plaintext for lookup
- Integrity: GCM produces an authentication tag that detects any tampering with the ciphertext
- At-rest protection: The SQLite database stores only ciphertext. Without the master key in memory, secrets are unreadable
Step 1: Store a credential
Use browse_vault_store to add a credential. Each credential requires a domain, kind, identity, and secret value.
Store a password
{
"tool": "browse_vault_store",
"arguments": {
"domain": "github.com",
"kind": "password",
"identity": "jane.smith@email.com",
"secret": "my-secure-password-123"
}
}Response:
Credential stored: a1b2c3d4-e5f6-7890-abcd-ef1234567890 (jane.smith@email.com@github.com, Password)Store an API key
{
"tool": "browse_vault_store",
"arguments": {
"domain": "api.openai.com",
"kind": "api_key",
"identity": "project-alpha",
"secret": "sk-abc123def456..."
}
}Store an OAuth token
{
"tool": "browse_vault_store",
"arguments": {
"domain": "linkedin.com",
"kind": "oauth_token",
"identity": "jane-linkedin",
"secret": "eyJhbGciOiJSUzI1NiIs..."
}
}Store a TOTP seed
TOTP seeds are the base32-encoded secret from your authenticator app setup (the value behind the QR code):
{
"tool": "browse_vault_store",
"arguments": {
"domain": "github.com",
"kind": "totp_seed",
"identity": "jane.smith@email.com",
"secret": "JBSWY3DPEHPK3PXP"
}
}Store a session cookie
{
"tool": "browse_vault_store",
"arguments": {
"domain": "dashboard.example.com",
"kind": "session_cookie",
"identity": "admin-session",
"secret": "sid=abc123; path=/; httponly; secure"
}
}Store a generic secret
For anything that does not fit the other categories:
{
"tool": "browse_vault_store",
"arguments": {
"domain": "internal.example.com",
"kind": "generic",
"identity": "database-connection-string",
"secret": "postgresql://user:pass@localhost:5432/mydb"
}
}Supported credential kinds
| Kind | Use case |
|---|---|
password | Login passwords |
api_key | API authentication tokens |
oauth_token | OAuth/SSO bearer tokens |
totp_seed | Base32 TOTP secret for 2FA code generation |
session_cookie | Session cookies for pre-authenticated access |
generic | Any other secret value |
Step 2: Retrieve a credential
Use browse_vault_get to decrypt and retrieve a stored credential:
{
"tool": "browse_vault_get",
"arguments": {
"domain": "github.com"
}
}This returns the first credential matching the domain, including the decrypted secret value.
To filter by credential type:
{
"tool": "browse_vault_get",
"arguments": {
"domain": "github.com",
"kind": "totp_seed"
}
}Step 3: List all credentials
View all stored credentials without decrypting secrets:
{
"tool": "browse_vault_list"
}Response:
4 credential(s):
a1b2c3d4 | github.com | Password | jane.smith@email.com | 5 uses
e5f6g7h8 | github.com | TotpSeed | jane.smith@email.com | 3 uses
i9j0k1l2 | api.openai.com | ApiKey | project-alpha | 12 uses
m3n4o5p6 | linkedin.com | OauthToken | jane-linkedin | 1 usesThe list shows IDs, domains, kinds, identities, and usage counts. Secret values stay encrypted.
Step 4: Automate login with stored credentials
The browse_login tool performs a complete login flow: navigate to the login page, fill credentials, submit, and follow redirect chains.
{
"tool": "browse_login",
"arguments": {
"url": "https://github.com/login",
"username": "jane.smith@email.com",
"password": "my-secure-password-123"
}
}This tool:
- Navigates to the login URL
- Detects username and password fields automatically
- Fills both fields
- Clicks the submit button
- Follows the entire redirect chain (302 -> 302 -> 200)
- Captures all
Set-Cookieheaders at every hop - Returns the final page snapshot and all cookies
Combined workflow: vault retrieval + login
A typical pattern is to retrieve the credential from the vault, then use it to log in:
// Step 1: Get the credential
{
"tool": "browse_vault_get",
"arguments": {
"domain": "github.com",
"kind": "password"
}
}
// Step 2: Use the returned username and password in browse_login
{
"tool": "browse_login",
"arguments": {
"url": "https://github.com/login",
"username": "jane.smith@email.com",
"password": "my-secure-password-123"
}
}Detect authentication type first
Not sure what kind of login the site uses? Run auth detection:
{
"tool": "auth_detect",
"arguments": {
"url": "https://example.com/login"
}
}This reports detected mechanisms: password forms, OAuth/SSO buttons, TOTP inputs, and CAPTCHA challenges.
Step 5: Generate TOTP 2FA codes
If you stored a totp_seed for a domain, generate a current 6-digit TOTP code:
{
"tool": "browse_vault_totp",
"arguments": {
"domain": "github.com"
}
}Response:
TOTP code for github.com: 482916The code is valid for the current 30-second window. Use it immediately with browse_fill on the 2FA input field:
{
"tool": "browse_fill",
"arguments": {
"ref_id": 5,
"text": "482916"
}
}Complete 2FA login flow
// Step 1: Log in with username and password
{
"tool": "browse_login",
"arguments": {
"url": "https://github.com/login",
"username": "jane.smith@email.com",
"password": "my-secure-password-123"
}
}
// Step 2: The login redirects to a 2FA page. Snapshot it.
{
"tool": "browse_snapshot"
}
// Step 3: Generate the TOTP code
{
"tool": "browse_vault_totp",
"arguments": { "domain": "github.com" }
}
// Step 4: Fill the 2FA code into the input field
{
"tool": "browse_fill",
"arguments": { "ref_id": 5, "text": "482916" }
}
// Step 5: Submit the 2FA form
{
"tool": "browse_submit_form",
"arguments": { "ref_id": 6 }
}Step 6: Rotate credentials
When you change a password or regenerate an API key, update the vault without deleting and re-creating:
{
"tool": "browse_vault_rotate",
"arguments": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"new_secret": "my-new-password-456"
}
}Get the credential ID from browse_vault_list.
Step 7: Delete credentials
Remove a credential permanently by its UUID:
{
"tool": "browse_vault_delete",
"arguments": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}This is a destructive operation -- the credential is removed from the database and cannot be recovered.
Step 8: Audit vault access
Every vault operation is logged. View the audit trail:
{
"tool": "browse_vault_audit",
"arguments": {
"limit": 20
}
}Response:
5 audit entries:
2026-03-22T10:00:00Z | STORE | github.com | OK
2026-03-22T09:55:00Z | GET | github.com | OK
2026-03-22T09:50:00Z | TOTP | github.com | OK
2026-03-22T09:45:00Z | ROTATE | api.openai.com | OK
2026-03-22T09:40:00Z | LIST | * | OKThe audit log records the operation type, target domain, timestamp, and status. Use it to verify that credentials are only accessed when expected.
Step 9: Domain approval controls
Restrict which domains can use a credential. This prevents a credential from being accidentally used on the wrong site.
Approve a domain
{
"tool": "vault_approve_domain",
"arguments": {
"credential_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"domain": "github.com"
}
}Check if a domain is approved
{
"tool": "vault_check_approval",
"arguments": {
"credential_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"domain": "github.com"
}
}Revoke domain access
{
"tool": "vault_revoke_domain",
"arguments": {
"credential_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"domain": "github.com"
}
}Step 10: Lock and unlock the vault
Lock the vault
Zeroize the master key from memory so no further decryption is possible until unlock:
{
"tool": "vault_lock"
}Unlock the vault
{
"tool": "vault_unlock",
"arguments": {
"passphrase": "my-vault-passphrase"
}
}In MCP mode, the vault auto-unlocks with an empty passphrase by default. To use a custom passphrase, set it when you first create the vault or unlock with the passphrase after locking.
Persisting cookies after login
After a successful browse_login, save the session cookies so you can restore them later without logging in again:
{
"tool": "cookie_save",
"arguments": {
"path": "~/.wraith/cookies.json"
}
}Next session, load them back:
{
"tool": "cookie_load",
"arguments": {
"path": "~/.wraith/cookies.json"
}
}Tips
- Store TOTP seeds early. Add the
totp_seedwhen you set up 2FA on a site, not when you need it in an emergency. - Use domain approvals for sensitive credentials. Restricting a credential to specific domains adds a safety layer against accidental use.
- Rotate, do not delete and recreate.
browse_vault_rotatepreserves the credential UUID, audit history, and domain approvals. - Lock the vault when idle. If you are done with a session,
vault_lockclears the master key from memory immediately. - Check the audit log periodically. It is the fastest way to verify nothing unexpected accessed your credentials.
- The vault is local only. All data stays in
~/.wraith/vault.dbon your machine. Nothing is sent to any server.