A simply utility for loading encrypted secrets from an age encrypted file.
This utility can be required by other plugins as a dependency to load a secret from your dotfiles to pass to a plugin.
For example, the llm.nvim plugin requires an API key. For those people who keep their dotfiles public but want to load an API key for its setup.
I take no responsibility for leaked passwords or API keys. It is on you to decide what age is, this approach, and making sure to not commit your identity keys. Inspired by pass and passage
- Generate a private key not in directory tracked by git.
mkdir ~/.age
cd ~/.age
age-keygen -o identity.txtThe contents of this identity.txt file will look like this (Taken from the
tests directory in this project for example).
# created: 2023-10-14T17:25:45+01:00
# public key: age1kk84zfvr4whsfdmrg2hk8pf7l3v705a8x9k5khuh0lmtrnuaz4gqnwty3q
AGE-SECRET-KEY-1USUPGW9GDQAQEHM8X3JKQSJMN7YY5T26PXWFHWMRR4Y9TDAHMA5SJDRUUJ
- Encrypt your secret.
age_public_key='age1kk84zfvr4whsfdmrg2hk8pf7l3v705a8x9k5khuh0lmtrnuaz4gqnwty3q'
echo 'MY_SUPER_COOL_API_KEY' | age -e -r "$age_public_key" -o ~/.dotfiles/api_key.ageUsing lazy.nvim
This example is using the excellent ChatGPT.nvim.
In the use case that you just want to load secrets from a pre-encrypted file.
local identity = vim.fn.expand('$HOME/.age/identity.txt')
local secret = vim.fn.expand('$HOME/.dotfiles/chatgpt.age')
require("lazy").setup({
"jackMort/ChatGPT.nvim",
event = "VeryLazy",
dependencies = {
...
"KingMichaelPark/age.nvim" -- Add age dependency
}
config = function()
vim.env.OPENAI_API_KEY = require('age').get(secret, identity) -- Get secret
require("chatgpt").setup()
end,
}
})In the use case that you want to install it as a plugin to assign key maps
return {
"KingMichaelPark/age.nvim",
keys = {
{
"<leader>E",
function() require('age').encrypt() end,
mode = { "v" },
desc = "age [E]ncrypt the selected text",
}
}
}For users who prefer using Neovim's built-in package management features (available in Neovim 0.12+).
vim.pack.add({
src = "https://github.com/KingMichaelPark/age.nvim"
})
vim.keymap.set("v", "<leader>E", function()
require('age').encrypt()
end, { desc = "age [E]ncrypt the selected text" })
local identity = vim.fn.expand('$HOME/.age/identity.txt')
local secret = vim.fn.expand('$HOME/.dotfiles/chatgpt.age')
-- Ensure the plugin is loaded before calling the 'get' function
vim.env.OPENAI_API_KEY = require('age').get(secret, identity)At the moment there are 4 options for decryption, I have added SOPs decrypt as
an option for json only as there is a built-in json decoder in neovim.
local age = require('age')
age.get(
"super-secret.txt.age", -- The file to decrypt
"identity.txt" -- Your private age key
)
age.list(
"super-secrets-multiline.txt.age", -- The file to decrypt
"identity.txt" -- Your private age key
)
-- Returns a table
age.from_json(
"super-secrets.json.age", -- The file to decrypt
"identity.txt" -- Your private age key
)
-- Returns a table
age.from_sops("tests/sops_api_keys.json")
-- SOPs uses a special path so you don't need to pass your identity
-- Encrypts the current visual selection
-- Prompts for confirmation and recipients (comma separated public keys)
age.encrypt()To run tests:
- Open neovim
- Have plenary.nvim installed
- Run
:PlenaryBustedDirectory tests/
Contributions are always welcome!