Clients

Python

The Python client is what your application uses to get its secrets: it loads a vault and hands your code the secrets you ask for.

Install it

Requires Python 3.9 or later.

The package is installed as valt0-client and imported as valt0.

pip install valt0-client

Open a vault

A vault is opened by its id; the name is a label you choose.

valt0.open_vault loads a vault from the local service. The service can only open a vault that has been imported into it, so run valt0 import on the vault first.

It raises an error if the service cannot be reached or the vault cannot be loaded.

Python
import valt0

vault = valt0.open_vault(id="your-vault-id", name="your-vault-name")

Read a secret

Ask for a secret by name, use it, then destroy it.

vault.secret(name) fetches one secret and returns a Secret. Use it in a with block, which zeroes the secret's buffer when the block exits. Take the value out as bytes where you can: bytes() hands back the secret's own buffer, so the with block wipes it too.

If you need the secret as a string use reveal().

Python
# The with block zeroes the underlying buffer as soon as you are finished
with vault.secret("DATABASE_PASSWORD") as secret:
    # Preferred: the raw bytes, wiped on exit
    pw = secret.bytes()

    # Or, when a string is unavoidable
    db.connect(password=secret.reveal())

Without a with block, call destroy() yourself, in a finally so it runs even when something fails:

Python
secret = vault.secret("DATABASE_PASSWORD")
try:
    db.connect(password=secret.reveal())
finally:
    secret.destroy()

Anywhere else, a Secret prints as [REDACTED] rather than its value: str(), repr(), format(), an f-string and a log line alike. Pickling or copying one raises TypeError, so a secret cannot slip out through multiprocessing or a cache. The value comes out only where you ask for it, with reveal() or bytes().

Python
print(secret)                            # [REDACTED]
print(repr(secret))                      # Secret([REDACTED])
f"{secret}"                              # [REDACTED]
logging.info("connecting %s", secret)    # connecting [REDACTED]
json.dumps({"pw": secret}, default=str)  # {"pw": "[REDACTED]"}
pickle.dumps(secret)                     # raises TypeError

secret.reveal()                          # the actual value

Handle errors

The two ways a connection fails have errors of their own.

open_vault and vault.secret raise valt0.CantConnectToServiceError when the service's socket or session file does not exist, which usually means the service is not running: valt0 status tells you whether it is. They raise valt0.NoServiceResponseError when the service did not answer within the one second the client waits.

Both are subclasses of valt0.Valt0Error, the base class of every error the client raises, so catch that last for anything else, such as a vault that could not be loaded.

Python
try:
    vault = valt0.open_vault(id="your-vault-id", name="your-vault-name")
except valt0.CantConnectToServiceError:
    ...  # socket or session file not found: is the valt0 service running?
except valt0.NoServiceResponseError:
    ...  # the service did not answer within the transport timeout (1s)
except valt0.Valt0Error:
    ...  # any other error, e.g. the vault could not be loaded

Threads and asyncio

The client is synchronous and safe to share between threads.

Every call uses its own connection, so one vault can be used from several threads at once. From async code, run the call in a worker thread with asyncio.to_thread.

Python
secret = await asyncio.to_thread(vault.secret, "DATABASE_PASSWORD")

API

open_vault(*, id, name): Vault

Opens the vault identified by id. Both arguments are keyword-only. Raises an error if the service cannot be reached or the vault cannot be loaded; the two connection failures are CantConnectToServiceError and NoServiceResponseError.

Options

idstr
The vault's id, the one valt0 info prints. It is the only value used to find the vault.
namestr
A label for the vault, used in messages.

Vault

The opened vault.

Fields

id
The vault's id.
name
The vault's name.

Methods

secret(name)Secret
Fetches one secret by name.

Secret

One secret, fetched from a vault. Used in a with block, it calls destroy() when the block exits.

Methods

bytes()bytearray
The raw bytes: the secret's own buffer, not a copy. Preferable to use where you can, because destroy() wipes it.
reveal()str
The value as a string. A string cannot be wiped, so use it sparingly. Raises ValueError if the value is not UTF-8 text.
destroy()
Zeroes the buffer.