Locker Secrets Python SDK
The official Python SDK for reading and managing secrets in Locker Secrets Manager.
The PyPI distribution is named lockersm; the Python import package is named
locker. Version 2 communicates with the Locker CLI through the stable
locker.sdk JSON-RPC protocol instead of parsing human-facing command output.
It supports both Locker Cloud and self-hosted Locker deployments.
Install
python -m pip install --upgrade lockersm
You do not need to install the Locker CLI separately in the standard managed mode. The SDK downloads a supported binary on first use and verifies the release signature, SHA-256 digest, platform, architecture, and protocol compatibility before execution.
Support matrix
| Component | Supported versions |
|---|---|
| Python | 3.10, 3.11, 3.12, 3.13, 3.14 |
| Linux managed CLI | x86-64, ARM64 |
| macOS managed CLI | Intel, Apple silicon |
| Windows managed CLI | x86-64 |
| SDK protocol | locker.sdk v1 |
For an air-gapped or centrally managed deployment, provide an explicit Locker
CLI path with LOCKER_CLI_PATH or binary_path.
Quick start
Create an access key in your Locker Secrets project, then expose the credentials to the application environment.
Linux and macOS:
export LOCKER_ACCESS_KEY_ID="your-access-key-id"
export LOCKER_SECRET_ACCESS_KEY="your-secret-access-key"
Windows PowerShell:
$env:LOCKER_ACCESS_KEY_ID = "your-access-key-id"
$env:LOCKER_SECRET_ACCESS_KEY = "your-secret-access-key"
Read a required secret:
from locker import Locker
client = Locker.from_env()
database_password = client.get_required(
"DATABASE_PASSWORD",
environment_name="production",
)
# Pass database_password directly to the component that needs it.
# Never print or log secret values.
get_required() raises ResourceNotFoundError when the key does not exist.
Use get() only when a fallback is intentionally safe:
log_level = client.get(
"LOG_LEVEL",
environment_name="production",
default_value="info",
)
The default value is returned only for a genuine not-found response. Authentication, permission, network, protocol, storage, and server failures are still raised.
Configure the client
Locker.from_env() is the recommended constructor. It recognizes:
| Environment variable | Purpose |
|---|---|
LOCKER_ACCESS_KEY_ID |
Project access key ID |
LOCKER_SECRET_ACCESS_KEY |
Project secret access key |
LOCKER_API_BASE |
Cloud or self-hosted API base URL |
LOCKER_CLI_PATH |
Absolute path to a deployment-managed CLI |
LOCKER_LOG |
debug, info, warning, or error |
The default cloud endpoint is
https://api.locker.io/locker_secrets.
Self-hosted deployment:
from locker import Locker
client = Locker.from_env(
api_base="https://secrets.example.com/locker_secrets",
)
Deployment-managed CLI:
from locker import Locker
client = Locker.from_env(
binary_path="/opt/locker/bin/locker",
)
The CLI path must be absolute and point to a regular, non-link file. Explicit
paths bypass managed updates and are never resolved through ambient PATH.
For migration from version 1, the SDK still recognizes ACCESS_KEY_ID,
SECRET_ACCESS_KEY, LOCKER_ACCESS_KEY_SECRET, and ACCESS_KEY_SECRET.
New deployments should use only the canonical LOCKER_* names.
Secrets
Read secret metadata
retrieve() returns the complete secret resource object:
secret = client.retrieve(
"DATABASE_PASSWORD",
environment_name="production",
)
print(secret.id, secret.key, secret.environment_name)
Secret objects contain plaintext values. Do not serialize, print, or include them in logs.
List secrets
for secret in client.list(environment_name="production"):
print(secret.id, secret.key, secret.environment_name)
For large projects, use bounded cursor pagination:
cursor = None
while True:
page = client.list_page(
environment_name="production",
page_size=100,
cursor=cursor,
)
for secret in page.items:
print(secret.id, secret.key)
cursor = page.next_cursor
if cursor is None:
break
Create and update secrets
Read secret input without echoing it in a terminal:
from getpass import getpass
created = client.create(
key="PAYMENT_API_KEY",
value=getpass("New secret value: "),
environment_name="staging",
)
updated = client.modify(
key=created.key,
value=getpass("Updated secret value: "),
environment_name="staging",
)
Secret values are sent to locker sdk in the JSON request on standard input.
They are not placed in process arguments or logs.
Export secrets
export() returns a plaintext str in dotenv or compact json format:
dotenv_payload = client.export(
environment_name="production",
output_format="dotenv",
)
Treat the returned string as sensitive. Avoid logs, shell arguments, command history, and unprotected files.
Secret deletion is not part of protocol v1. The canonical Locker client does
not expose a delete method; legacy resource-object delete helpers fail closed
with InvalidRequestError.
Environments
environments = client.list_environments()
for environment in environments:
print(environment.name, environment.external_url)
Use list_environments_page() for cursor pagination.
The two lookup methods intentionally have different not-found contracts:
get_environment("production")returnsNone.retrieve_environment("production")raisesResourceNotFoundError.
Create or update an environment:
created = client.create_environment(
name="staging",
external_url="https://staging.example.com",
)
updated = client.modify_environment(
name=created.name,
external_url="https://new-staging.example.com",
)
Environment deletion is not part of protocol v1.
Errors
Locker-defined transport, protocol, authentication, and API failures derive
from locker.error.LockerError. Standard Python argument errors can still be
raised for locally invalid values.
import logging
from locker.error import LockerError, RateLimitError
try:
value = client.get_required("PAYMENT_API_KEY")
except RateLimitError as exc:
logging.warning(
"Locker request was rate limited (request_id=%s, retryable=%s)",
exc.request_id,
exc.retryable,
)
raise
except LockerError as exc:
logging.error(
"Locker request failed (type=%s, code=%s, kind=%s, request_id=%s)",
type(exc).__name__,
exc.code,
exc.kind,
exc.request_id,
)
raise
| RPC code | Exception | Meaning |
|---|---|---|
-32001 |
AuthenticationError |
Credentials were rejected |
-32003 |
PermissionDeniedError |
Access is not permitted |
-32004 |
ResourceNotFoundError |
Resource does not exist |
-32029 |
RateLimitError |
Request was rate limited |
-32050 |
APIConnectionError |
Locker API could not be reached |
-32051 |
APIServerError |
Locker API failed the request |
-32060 |
LocalStorageError |
Local secure state failed |
-32700 to -32600 |
ProtocolError |
Invalid JSON-RPC exchange |
Each exception exposes code, kind, retryable, and request_id. Avoid
logging exception bodies or application data around a secret operation.
CLI management and verification
Importing locker and constructing Locker are side-effect free. The SDK
does not create a cache directory, access the network, or start a process until
the first operation or an explicit install_cli() call.
Managed mode:
- Checks the signed release channel on first use and at most once every six hours after a successful check.
- Verifies the embedded Locker Ed25519 trust root, signed latest document, signed manifest, artifact size, SHA-256, detached signature, executable header, and protocol range.
- Installs releases into immutable
~/.locker/sdk-cli/python/bin/releases/<version>/directories. - Switches the current pointer only after complete verification.
- Rejects rollback attempts and same-version mutation.
Force an immediate signed update check:
installed_path = client.install_cli()
If the release service is temporarily unreachable, a previously accepted binary may be reused only after its cached metadata and artifact are verified again. Invalid signatures, rollback state, incompatible platforms, and protocol errors always fail closed.
Logging
Set LOCKER_LOG or the constructor's log argument:
export LOCKER_LOG="info"
client = Locker.from_env(log="info")
SDK logs contain operational metadata such as method, request ID, duration, and process status. The SDK never emits credentials, request or response bodies, custom headers, or secret values.
Migrating to 2.x
Version 2:
- Requires Python 3.10 or newer.
- Uses the stable
locker.sdkJSON-RPC protocol v1. - Downloads only signed Locker CLI releases in managed mode.
- Uses canonical
LOCKER_ACCESS_KEY_IDandLOCKER_SECRET_ACCESS_KEYvariables. - Adds
Locker.from_env(),get_required(), typed pagination, and explicit CLI lifecycle methods. - Returns a default value only for
ResourceNotFoundError.
Applications must use this SDK or the locker sdk protocol. Do not parse
human-facing CLI output.
Versioning and releases
The package follows Semantic Versioning and publishes canonical PEP 440 versions:
- PyPI package:
MAJOR.MINOR.PATCH - Git tag and GitLab Release:
vMAJOR.MINOR.PATCH
Every accepted merge commit on protected main automatically runs the complete
release flow: derive version, build deterministic wheel and source
distribution, verify metadata and the CLI trust root, publish to PyPI, and
create the matching source tag and GitLab Release. There is no manual publish
or pre-created tag step.
The version is deterministic for the commit's first-parent position on
main. Retrying the same pipeline reuses the same version, and concurrent
main pipelines cannot assign the same version. Release builds and source tags
share the version through setuptools-scm, so installing a tag reproduces its
published package version. CI rejects direct, fast-forward, rebase, or
single-parent updates to the release line.
Development
Install the reviewed development toolchain:
python -m pip install -r requirements-dev.txt
Run the supported-Python test matrix:
tox
Useful focused commands:
tox -e py310
tox -e lint
tox -e type
python scripts/verify_ci_supply_chain.py
Integration tests are opt-in. A protocol handshake needs an explicit released CLI:
LOCKER_TEST_CLI_PATH=/absolute/path/to/locker
Live vault tests additionally require:
LOCKER_RUN_INTEGRATION=1
LOCKER_TEST_ACCESS_KEY_ID=your-test-access-key
LOCKER_TEST_SECRET_ACCESS_KEY=your-test-secret-key
Run them with tox -e integration.
Security
If you discover a security issue, email contact@locker.io. Do not disclose vulnerabilities in a public issue.
General product documentation and support are available at support.locker.io.
License
Copyright CyStack Corporation.
Licensed under the Apache License 2.0. Locker is developed and maintained by CyStack.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lockersm-2.0.0.tar.gz.
File metadata
- Download URL: lockersm-2.0.0.tar.gz
- Upload date:
- Size: 59.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b75f3d26bbab435eeb47f05f38567f9e49190939c71bb83a6c447bf4e6a0edcf
|
|
| MD5 |
f04a9985def199d7565cf3be684c3f8b
|
|
| BLAKE2b-256 |
6ab4c6f7cf4de09d92c9d95d5eb501946a903dc8e103ce6b235c68f0050c9412
|
File details
Details for the file lockersm-2.0.0-py3-none-any.whl.
File metadata
- Download URL: lockersm-2.0.0-py3-none-any.whl
- Upload date:
- Size: 64.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0da13a5887771689687bf9c9545409be2ae2d9e37fff04fad277eaabdb79d0a3
|
|
| MD5 |
f1c1fd2aa3f27f72fd8e45123f39ee7e
|
|
| BLAKE2b-256 |
397875e19f5df551b81d56f442695f02127ad11ec36dda335d979620f196668d
|