databox.nvim is a robust and secure Neovim plugin that provides encrypted storage for Lua tables (dictionaries), using age or compatible encryption tools for cryptographic safety. Data is stored deeply encrypted, meaning every string โ including nested keys and values โ is protected.
mktemp command){
"chrisgve/databox.nvim",
config = function()
local success, err = require("databox").setup({
private_key = "~/.config/age/keys.txt",
public_key = "age1example...", -- Your public key string
-- Optional: Use rage for better performance
-- encryption_cmd = "rage -e -r %s",
-- decryption_cmd = "rage -d -i %s",
})
if not success then
vim.notify("Databox setup failed: " .. err, vim.log.levels.ERROR)
end
end,
}
Using your preferred plugin manager, then configure in your init.lua:
-- Example with packer.nvim
use {
'chrisgve/databox.nvim',
config = function()
local success, err = require("databox").setup({
private_key = "~/.config/age/keys.txt",
public_key = "age1example...",
})
if not success then
print("Databox setup failed: " .. err)
end
end
}
Generate your age key pair:
# Create age directory
mkdir -p ~/.config/age
# Generate key pair (works with both age and rage)
age-keygen -o ~/.config/age/keys.txt
# Your public key will be displayed in the terminal
# Example: age1abc123def456ghi789jkl012mno345pqr678stu901vwx234yz567890ab
The public key (starting with age1...) is what you'll use in your configuration.
private_key (string): Path to your private key filepublic_key (string): Your public key string or path to public key filestore_path (string): Custom storage path (defaults to XDG_DATA_HOME or ~/.local/share/nvim/databox.txt)encryption_cmd (string): Command template for encryption (default: "age -e -a -r %s" - note the -a flag for ASCII armor)decryption_cmd (string): Command template for decryption (default: "age -d -i %s")require("databox").setup({
private_key = "~/.config/age/keys.txt",
public_key = "age1abc123def456ghi789jkl012mno345pqr678stu901vwx234yz567890ab",
})
require("databox").setup({
private_key = "~/.config/age/keys.txt",
public_key = "age1abc123def456ghi789jkl012mno345pqr678stu901vwx234yz567890ab",
encryption_cmd = "rage -e -a -r %s", -- Note: -a flag for ASCII armor
decryption_cmd = "rage -d -i %s",
})
require("databox").setup({
private_key = "~/.config/age/keys.txt",
public_key = "age1abc123def456ghi789jkl012mno345pqr678stu901vwx234yz567890ab",
store_path = "~/my-project/.secrets.txt",
})
require("databox").setup({
private_key = "~/.config/mycrypt/key.pem",
public_key = "~/.config/mycrypt/pub.pem",
encryption_cmd = "mycrypt encrypt --key %s",
decryption_cmd = "mycrypt decrypt --key %s",
})
All functions return (success: boolean, error?: string) for operations that can fail.
local db = require("databox")
-- Check if plugin is working
local success, err = db.setup({ ... })
-- Set a new key (fails if key exists)
local ok, err = db.set("project1", {
token = "secret123", -- String: encrypted
max_requests = 1000, -- Number: encrypted (protects limits/quotas)
debug_enabled = true, -- Boolean: encrypted (protects config flags)
config = {
lang = "lua",
timeout = 30, -- Nested number: encrypted
features = {}, -- Empty table: encrypted and preserved
disabled_feature = nil -- nil value: encrypted and preserved
}
})
-- Update existing key (fails if key doesn't exist)
local ok, err = db.update("project1", { token = "newsecret456" })
-- Get value (returns value, error)
local value, err = db.get("project1")
-- Check existence
local exists = db.exists("project1") -- true/false
-- Remove key
local ok, err = db.remove("project1")
-- Get all keys
local all_keys = db.keys() -- returns string[]
-- Clear all data
local ok, err = db.clear()
-- Manual save/load
local ok, err = db.save()
local ok, err = db.load()
Most operations auto-save by default. Use save = false to batch operations:
-- Batch operations without saving each time
db.set("key1", "value1", false)
db.set("key2", "value2", false)
db.set("key3", "value3", false)
-- Save all at once
local ok, err = db.save()
Encrypted data is stored in:
$XDG_DATA_HOME/nvim/databox.txt
If XDG_DATA_HOME is not set, it defaults to:
$HOME/.local/share/nvim/databox.txt
"secret" โ encrypted42 โ encrypted (protects IDs, seeds, limits, etc.)true โ encrypted (protects configuration flags)nil โ encrypted (preserves intentional nil values){} โ encrypted (preserves empty structures)-a flag to produce UTF-8 safe encrypted output for JSON storagemktemp for unpredictable temporary file namesThe plugin uses per-string encryption by design - this isn't inefficient, it's a security feature:
The plugin provides detailed error messages for common issues:
local ok, err = db.set("existing_key", "value")
if not ok then
print("Error: " .. err) -- "Key already exists: existing_key"
end
local ok, err = db.update("nonexistent_key", "value")
if not ok then
print("Error: " .. err) -- "Key does not exist: nonexistent_key"
end
local ok, err = db.set("test", function() end)
if not ok then
print("Error: " .. err) -- "Cannot serialize function values"
end
mktemp command%s placeholder for key parameter"Plugin not initialized"
setup() is called before using any other functionsprivate_key and public_key are provided"Command failed" or encryption/decryption errors
echo "test" | age -e -a -r <your_public_key> (note the -a flag)"Failed to create secure temporary file"
mktemp command is available/tmp directory is writablePerformance issues
age to rage for 2-3x performance improvementsave = false for batch operations to reduce I/OEnable verbose error output:
-- Check setup status
local success, err = require("databox").setup({ ... })
if not success then
vim.notify("Setup failed: " .. err, vim.log.levels.ERROR)
end
-- Check individual operations
local ok, err = db.set("test", "value")
if not ok then
vim.notify("Set failed: " .. err, vim.log.levels.ERROR)
end
Test your encryption utility choice:
# Test age performance with ASCII armor
time echo "test data" | age -e -a -r <your_public_key> | age -d -i <your_private_key>
# Test rage performance with ASCII armor
time echo "test data" | rage -e -a -r <your_public_key> | rage -d -i <your_private_key>
| Tool | Language | Performance | Compatibility | Installation |
|---|---|---|---|---|
| age | Go | Standard | Universal | brew install age / package managers |
| rage | Rust | 2-3x faster | age-compatible | cargo install rage / releases |
| Custom | Any | Varies | Must be age-compatible | Your choice |
age (default)rage with custom commands-a flag) for UTF-8 safe JSON storageMIT โ ยฉ 2025 @chrisgve