Skip to main content

Universal .env variable manager — read, write, encrypt, delegate across services and devices

Project description

getv — Universal .env Variable Manager

PyPI version Python versions License: Apache-2.0 Downloads Tests

Read, write, encrypt, and delegate environment variables across services and devices.

📑 Table of Contents

Why getv?

Every project reinvents .env parsing. getv provides one library for:

  • Reading/writing .env files with comment preservation
  • Profile management — named configs for devices, LLM providers, databases
  • Secret masking — automatic detection and masking of passwords/keys in logs
  • Encryption — Fernet-based encryption of sensitive values for safe transport
  • Format export — dict, JSON, shell, docker-compose, pydantic BaseSettings
  • CLI — manage profiles from the command line

Install

pip install getv                   # core
pip install "getv[crypto]"         # + encryption (Fernet)
pip install "getv[pydantic]"       # + pydantic BaseSettings export
pip install "getv[all]"            # everything

Quick Start

Python API

from getv import EnvStore, ProfileManager

# Single .env file
store = EnvStore("~/.myapp/.env")
store.set("DB_HOST", "localhost").set("DB_PORT", "5432").save()
print(store.get("DB_HOST"))  # "localhost"

# Profile manager — multiple named configs
pm = ProfileManager("~/.getv")
pm.add_category("devices", required_keys=["RPI_HOST", "RPI_USER"])
pm.add_category("llm", required_keys=["LLM_MODEL"])

pm.set("devices", "rpi3", {
    "RPI_HOST": "192.168.1.10",
    "RPI_USER": "pi",
    "RPI_PASSWORD": "secret",
    "RPI_PORT": "22",
})

pm.set("llm", "groq", {
    "LLM_MODEL": "groq/llama-3.3-70b-versatile",
    "GROQ_API_KEY": "gsk_xxx",
})

# Merge profiles on top of base config
base = {"APP_NAME": "myapp", "RPI_HOST": "default"}
cfg = pm.merge_profiles(base, devices="rpi3", llm="groq")
# cfg["RPI_HOST"] == "192.168.1.10" (overridden by device profile)
# cfg["LLM_MODEL"] == "groq/llama-3.3-70b-versatile"

# App-specific defaults
from getv.app_defaults import AppDefaults
defaults = AppDefaults("myapp")
defaults.set("llm", "groq").set("devices", "rpi3")
# Later: cfg = pm.merge_profiles(base, **defaults.as_profile_kwargs())

CLI

# Set variables
getv set devices rpi3 RPI_HOST=192.168.1.10 RPI_USER=pi RPI_PASSWORD=secret

# Get a single variable
getv get devices rpi3 RPI_HOST
# → 192.168.1.10

# List all categories
getv list
#   devices/ (2 profiles)
#   llm/ (3 profiles)

# List profiles in a category (secrets masked)
getv list devices
#   rpi3: RPI_HOST=192.168.1.10, RPI_USER=pi, RPI_PASSWORD=secr***

# Show all variables (unmasked)
getv list devices rpi3 --show-secrets

# Export to different formats
getv export devices rpi3 --format json
getv export devices rpi3 --format shell
getv export devices rpi3 --format pydantic
getv export llm groq --format docker

# Encrypt sensitive values (Fernet)
getv encrypt devices rpi3
# → Generated key: ~/.getv/.fernet.key
# → Encrypted sensitive values in devices/rpi3

# Decrypt
getv decrypt devices rpi3

# Delete a profile
getv delete devices old-rpi

# Execute commands with profile environment
getv exec llm groq -- python my_script.py
getv exec devices rpi3 -- ssh pi@host uname -a

# SSH to devices using profile
getv ssh rpi3                    # interactive shell
getv ssh rpi3 "uname -a"        # run remote command

# Make authenticated API calls
getv curl groq https://api.groq.com/openai/v1/models
getv curl openai https://api.openai.com/v1/models -X POST -d '{"model":"gpt-4"}'

# Set app-specific defaults
getv use myapp llm groq
getv use myapp devices rpi3

# Show app defaults
getv defaults              # list all apps
getv defaults myapp       # show myapp defaults

Profile Directory Structure

~/.getv/                       ← GETV_HOME (configurable)
├── .fernet.key                ← encryption key (chmod 600)
├── defaults/                  ← per-app default profile selections
│   ├── fixpi.conf             → llm=groq, devices=rpi3
│   ├── prellm.conf            → llm=openrouter
│   └── marksync.conf          → llm=ollama-local
├── devices/
│   ├── rpi3.env
│   ├── rpi4-prod.env
│   └── nvidia.env
├── llm/
│   ├── groq.env
│   ├── openrouter.env
│   └── ollama-local.env
└── ollama/
    ├── local.env
    └── gpu-server.env

Each .env file is a standard KEY=VALUE file:

# ~/.getv/devices/rpi3.env
RPI_HOST=192.168.1.10
RPI_USER=pi
RPI_PASSWORD=secret
RPI_PORT=22

App Defaults

Each app remembers which profile to use — so fixpi uses Groq while marksync uses Ollama:

# Set defaults (one-time)
getv use fixpi llm groq
getv use fixpi devices rpi3
getv use prellm llm openrouter
getv use marksync llm ollama-local

# Check what's configured
getv defaults
#   fixpi: devices=rpi3, llm=groq
#   marksync: llm=ollama-local
#   prellm: llm=openrouter

# In your app startup code:
from getv import AppDefaults, ProfileManager
defaults = AppDefaults("fixpi")
pm = ProfileManager("~/.getv")
cfg = pm.merge_profiles({}, **defaults.as_profile_kwargs())

Integrations

getv ships with plugins for common tools:

SSH

# Setup once
getv set devices rpi3 RPI_HOST=192.168.1.10 RPI_USER=pi RPI_PASSWORD=raspberry

# Connect
getv ssh rpi3                       # interactive shell
getv ssh rpi3 "uname -a"            # remote command
from getv.integrations.ssh import SSHEnv
ssh = SSHEnv.from_profile("rpi3")
ssh.run("uname -a", capture=True)            # subprocess
params = ssh.as_paramiko_kwargs()            # for paramiko

LiteLLM

# Setup providers
getv set llm groq LLM_MODEL=groq/llama-3.3-70b-versatile GROQ_API_KEY=gsk_xxx
getv set llm openrouter LLM_MODEL=openrouter/google/gemini-2.0-flash-exp:free OPENROUTER_API_KEY=sk-or-xxx

# Switch at runtime
getv exec llm groq -- python my_script.py
getv exec llm openrouter -- python my_script.py
from getv.integrations.litellm import LiteLLMEnv
llm = LiteLLMEnv.from_profile("groq")
llm.activate()  # sets os.environ
# or: litellm.completion(**llm.as_completion_kwargs(), messages=[...])

Ollama

getv set ollama gpu-server OLLAMA_API_BASE=http://192.168.1.50:11434 OLLAMA_MODEL=qwen2.5-coder:14b
getv exec ollama gpu-server -- ollama run qwen2.5-coder:14b
from getv.integrations.ollama import OllamaEnv
oll = OllamaEnv.from_profile("gpu-server")
oll.activate()  # sets OLLAMA_API_BASE in env
print(oll.litellm_model())  # "ollama/qwen2.5-coder:14b"

Docker

getv export llm groq --format docker > /tmp/groq.env
docker run --env-file /tmp/groq.env my-llm-app:latest
from getv.integrations.docker import DockerEnv
denv = DockerEnv.from_profiles(llm="groq", devices="rpi3")
denv.write_env_file("/tmp/docker.env")
print(denv.compose_environment())  # docker-compose block

curl

# API call with auth from profile
getv curl groq https://api.groq.com/openai/v1/models
getv curl openai https://api.openai.com/v1/models

Pydantic Settings

from getv.integrations.pydantic_env import load_profile_into_env
load_profile_into_env("llm", "groq")  # inject into os.environ
settings = MySettings()               # pydantic reads from env

Subprocess / Pipe

# Run any command with profile env injected
getv exec llm groq -- python my_script.py
getv exec devices rpi3 -- ansible-playbook deploy.yml

# Shell eval
eval $(getv export llm groq --format shell)

One-liner Examples

Popular API Tokens

# OpenAI
export OPENAI_API_KEY=$(getv get llm openai OPENAI_API_KEY) && python my_script.py

# GitHub
git clone https://$(getv get git github GH_TOKEN)@github.com/user/repo.git

# AWS
export AWS_ACCESS_KEY_ID=$(getv get aws prod AWS_ACCESS_KEY_ID) && \
export AWS_SECRET_ACCESS_KEY=$(getv get aws prod AWS_SECRET_ACCESS_KEY) && \
aws s3 ls

# Docker Hub
echo $(getv get docker hub DOCKERHUB_TOKEN) | docker login --username user --password-stdin

# Slack
curl -X POST -H 'Authorization: Bearer '$(getv get chat slack SLACK_BOT_TOKEN) \
  -H 'Content-type: application/json' --data '{"text":"Hello"}' \
  https://slack.com/api/chat.postMessage

# Multiple env vars
eval "$(getv export llm openai --format shell)" && python my_script.py

# Docker compose
getv export app production --format env > .env && docker-compose up

# Direct API calls
getv curl openai https://api.openai.com/v1/models
getv curl groq https://api.groq.com/openai/v1/chat/completions -X POST -d '{"model":"llama3-70b"}'

Security

Automatic Secret Detection

Keys matching these patterns are automatically masked in display/logs:

PASSWORD, PASSWD, SECRET, TOKEN, API_KEY, APIKEY, PRIVATE_KEY, ACCESS_KEY, ACCESS_TOKEN, AUTH, CREDENTIAL

from getv.security import mask_dict, is_sensitive_key

data = {"RPI_HOST": "10.0.0.1", "RPI_PASSWORD": "secret123"}
print(mask_dict(data))
# {"RPI_HOST": "10.0.0.1", "RPI_PASSWORD": "secr***"}

Encryption for Transport

from getv.security import generate_key, encrypt_store, decrypt_store

key = generate_key()
data = {"RPI_HOST": "10.0.0.1", "RPI_PASSWORD": "secret"}
encrypted = encrypt_store(data, key, only_sensitive=True)
# {"RPI_HOST": "10.0.0.1", "RPI_PASSWORD": "ENC:gAAA..."}

original = decrypt_store(encrypted, key)
# {"RPI_HOST": "10.0.0.1", "RPI_PASSWORD": "secret"}

Format Export

Format Function Output
dict store.as_dict() {"KEY": "val"}
JSON to_json(data) {"KEY": "val"}
Shell to_shell_export(data) export KEY='val'
Docker to_docker_env(data) KEY=val
.env to_env_file(data) KEY=val
Pydantic to_pydantic_settings(data) Python class source
Pydantic model to_pydantic_model(data) BaseSettings instance

CLI Reference

Command Description
getv set CATEGORY PROFILE KEY=VAL... Create/update a profile
getv get CATEGORY PROFILE KEY Get a single value
getv list [CATEGORY [PROFILE]] List categories, profiles, or vars
getv delete CATEGORY PROFILE Delete a profile
getv export CATEGORY PROFILE --format FMT Export (json/shell/docker/env/pydantic)
getv encrypt CATEGORY PROFILE Encrypt sensitive values
getv decrypt CATEGORY PROFILE Decrypt values
getv exec CATEGORY PROFILE -- CMD... Run command with profile env
getv use APP CATEGORY PROFILE Set app default profile
getv defaults [APP] Show app defaults
getv ssh PROFILE [CMD] SSH to device from profile
getv curl PROFILE URL Authenticated API call

Examples

See examples/ directory:

File Description
01_quick_start.py Centralized .env management
02_ssh_from_profile.py SSH/SCP with paramiko/fabric
03_litellm_multi_provider.py Switch LLM providers
04_ollama_config.py Ollama local/remote/Docker
05_docker_env.py Docker env files & compose
06_app_defaults.py Per-app default profiles
07_pipe_and_shell.sh Shell integration & pipes
08_pydantic_settings.py Pydantic Settings bridge

Environment Variables

Variable Default Description
GETV_HOME ~/.getv Base directory for profiles

Adopted by

Projects using getv for .env management:

  • fixpi — SSH + LLM diagnostic agent
  • prellm — LLM preprocessing proxy
  • code2logic — Code analysis engine
  • amen — Intent-iterative AI gateway
  • marksync — Markdown sync server
  • curllm — LLM-powered web automation

Development

git clone https://github.com/wronai/getv.git
cd getv
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest  # 84 tests

License

Apache License 2.0 - see LICENSE for details.

Author

Created by Tom Sapletta - tom@sapletta.com

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

getv-0.1.4.tar.gz (35.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

getv-0.1.4-py3-none-any.whl (33.7 kB view details)

Uploaded Python 3

File details

Details for the file getv-0.1.4.tar.gz.

File metadata

  • Download URL: getv-0.1.4.tar.gz
  • Upload date:
  • Size: 35.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for getv-0.1.4.tar.gz
Algorithm Hash digest
SHA256 5b03c6da6664e8510c7e1a1102ebf05a884f2bfba73b213bb1fedc07d15d453f
MD5 ce246bbf034b12af514545ba13d249ca
BLAKE2b-256 e43680cb7c5d7ba03535fba443bde27bca69d697bd1fcbd662c75102bd3391f3

See more details on using hashes here.

File details

Details for the file getv-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: getv-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 33.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for getv-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 c4ba4934394ea64b559d15714c857934d9c310b75b8848445d9414ac4780ec33
MD5 a96b1742a1854c8594f1233155ebc2f0
BLAKE2b-256 7bf99ccaa68547e8b800132b00d50518cfabb8e09506a399758ba939edc069b4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page