Local tools should still use vaults
Engineers have always been obsessed with automating the minutia so we can focus on the fun stuff. But writing software has historically been A) hard B) expensive. That usually meant that we would spend a week automating what we could have done by hand in a few minutes. Or we wouldn't try at all.
I'm nowhere close to the first person to reflect that agents have changed that equation. Especially with self-feedback loops, agents can do a pretty damn good job on the first pass - and can certainly succeed at most of these minutia automations when given enough feedback and freedom to run in an environment with the right tools.
I've been trying to make a concerted effort to automate even the smallest of tasks, especially when they're annoyingly asynchronous. The most recent project1 was automating the import and alignment process of our podcast feeds after we record.2 Before that, some family image organization scripts. I probably have tens of adhoc scripts littered around my github at this point.
This has led to the proliferation of env files across my system, as I naturally incorporate the same design principles that I would use for a shipping system: pydantic-settings config definition, refactored package, etc. Except it's not a shipping system and I don't plan on it becoming one.
I realized that storing my credentials in some vault would be a better way; but most proper vaults (AWS Secrets Manager, Hashicorp Vault, etc) are meant for enterprise use. For local scripts I would much rather interplay with my keychain management platform 1Password.
Enter: vaultdantic, a super simple plugin for Pydantic settings.
from pydantic import SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict
from vaultdantic import OnePasswordConfigDict, VaultMixin
class ExampleSettings(BaseSettings, VaultMixin):
model_config = SettingsConfigDict(
env_prefix="EXAMPLE_",
)
model_vault_config = OnePasswordConfigDict(
vault="Engineering",
entry="example-service",
)
api_token: SecretStr
workspace_id: str
config = ExampleSettings()
It does exactly what you'd expect. On initialization, it will try to inspect your existing environment for env credentials. If it can't find the right ones it will fall back to accessing 1Password via your password or fingerprint.
I've started to switch over all my adhoc projects to use this convention. It ends up being really convenient as a default for these local scripts. Adding a new backend like Bitwarden or LastPass should be as simple as adding another subclass of the main provider. And if you ever need to eject and publish something to the cloud, you can do that too:
uv run sync-vault-to-env
This also takes me closer to a long held goal of mine to be able to freely wipe my laptop and not worry about data loss. I'm not confident I'm there yet, but a combination of NAS, version control, and these default vault settings is getting me meaningfully closer. Hope it's useful to you too.
Footnotes
-
Project is even giving it too much credit when you're just doing a few
codexcalls. Agent-launch? ↩ -
1. Import video from our two camcorders to our local NAS
2. Extract the audio files from our Audio Hijack recording session
3. Align audio files to the start time of the video files
4. Transcode the raw prores files into mp4 for our editor
5. Upload all artifacts to frame (our file sharing host)
This amounted to saving a few clicks after we recorded, but because some of this stuff can only happen synchronously (like hardware accelerated ffmpeg commands) it made for an annoying post-record workflow. ↩
/dev/newsletter
Technical deep dives on machine learning research, engineering systems, and building scalable products. Published weekly.
Unsubscribe anytime. No spam, promise.