Keep API secrets out of plaintext config: layered .ranbval* env files, decrypt vault tokens only when used, with built-in repo-allowlist enforcement and automatic usage telemetry to the Live Monitor—minimal deps, your own HTTP/SDK stack.
Project description
Ranbval SDK v2.0.0
The Python client for Ranbval — a secret manager for API keys. Encrypt secrets in the
Ranbval dashboard, store the encrypted tokens in .ranbval files, and decrypt them only at
runtime — AES-256-GCM with PBKDF2 key derivation, no plaintext ever touches source control.
Unlike a plain .env, a stolen config is useless off your allowlisted repos, and every use is
attributable in the Live Monitor.
pip install ranbval-sdk
Why Ranbval Exists
Every team now juggles a pile of API keys — LLM providers, payment processors, databases, third-party services. Those keys leak constantly, and almost always the same handful of ways:
- A key gets committed to Git — and bots scrape public repos within minutes.
- A
.envfile is copied and shared over Slack/email — then forwarded, forgotten, and lives forever with no expiry. - A key is accidentally printed to logs or captured by an error reporter — and now it sits in Datadog/Sentry, readable by the whole org, retained for years.
- When a key does leak, nobody knows who leaked it or which repo burned the tokens — so you can't rotate with confidence.
.env + load_dotenv() does nothing about any of this: the secret is plaintext on disk, works
anywhere it's copied, forever, with zero visibility. Ranbval is built to close exactly these
gaps.
What it actually protects (and what nothing can)
Be clear-eyed about the threat model — it's what makes the guarantees trustworthy:
- What no tool can stop: an attacker who already runs code inside your process. If they can
execute in your app, they can read
os.environ, hook functions, or dump memory — and no secret manager (Vault, AWS/GCP Secrets Manager, Doppler, Ranbval) prevents that. It isn't the real-world leak vector. - What Ranbval does stop — the leaks that actually happen:
| Real-world leak | .env |
Ranbval |
|---|---|---|
| Key committed to Git | 🔴 plaintext, public instantly | 🟢 encrypted token — a commit leaks nothing usable |
| Config file copied / shared | 🔴 works anywhere, forever | 🟢 useless without the project secret and an allowlisted repo |
| Key printed to logs / captured by Sentry | 🔴 sits in log storage for years | 🟢 SecretString masks every display path; can't be pickled into a cache/report |
| A key leaks — who? which repo? | 🔴 zero visibility | 🟢 Live Monitor flags the same credential on a new device/IP → rotate with proof |
The crown jewel is the repo allowlist: even if someone steals your entire .ranbval file
and your project secret, they still can't decrypt it from a repo that isn't on your
control-plane allowlist. A stolen config is a dead config.
An analogy
You can't make a house key that opens your door but that a thief holding it can't use — if the key opens the lock, whoever holds it gets in. That's physics, not a flaw. Real security comes from three other things, and Ranbval gives you all three:
- The key isn't lying in the street → plaintext never touches Git (encrypted tokens).
- The key only works at your house → the repo allowlist makes a stolen file worthless elsewhere.
- An alarm rings if a stranger walks in → leak detection alerts on a new device/IP.
Why use it
- Drop-in. One
load_ranbval()replaces scatteredload_dotenv(); keys pass straight into your existing SDKs — Ranbval ships no vendor dependencies. - Safe by default. Secrets are sealed
SecretStrings that refuse to print, log, or serialize; plain config is opt-in plaintext via[public]sections andpublic(). - Accountable. Every decrypt is attributable, and misuse is detectable — something a plain
.envcan never offer.
Quick Start
from ranbval_sdk import load_ranbval, decrypt_key
import os, openai
# 1. Load encrypted config from .ranbval files (no network, no decryption)
load_ranbval()
# 2. Decrypt a vault token — returns a SecretString, never printable.
# This also auto-reports the usage to your Live Monitor (no extra code).
api_key = decrypt_key("OPENAI_API_KEY")
# 3. Pass directly to the SDK — value is never exposed in logs or prints
client = openai.OpenAI(api_key=api_key.use())
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
)
.ranbval.local (never commit this file):
RANBVAL_PROJECT_SECRET=your_dashboard_project_secret
OPENAI_API_KEY=ranbval.4ii0a022aa.p1GOZ...ahsan
Module Reference
| Symbol | Description |
|---|---|
load_ranbval() |
Merges layered .ranbval* files into os.environ |
public() |
Read a plaintext (unencrypted) config value — never decrypts |
public_config() |
Dict of every key declared under [public] |
safe_decrypt() |
Decrypts a vault token string → SecretString |
decrypt_key() |
Reads an env var and decrypts it in one call |
SecretString |
Wrapper that blocks all display paths — value only via .use() |
proxy_request() |
Route an HTTP request through the Ranbval proxy (key injected server-side) |
emit_telemetry() |
Record a custom usage event (basic usage is auto-reported on every decrypt_key()) |
get_audit_log() |
Return the in-process audit log list |
clear_audit_log() |
Clear the in-process audit log |
get_project_key() |
Read RANBVAL_PROJECT_SECRET from env |
find_ranbval_file() |
Locate the nearest .ranbval* file on disk |
find_ranbval_directory() |
Locate the config root directory |
resolve_ranbval_mode() |
Determine the active mode from env/args |
Package Layout
Everything is organized by concern. You only import from the top level
(from ranbval_sdk import …); the table shows where each piece lives.
ranbval_sdk/
├── __init__.py # the public API (re-exports everything below)
├── exceptions.py # RanbvalError hierarchy
├── py.typed # ships type information (PEP 561)
├── config/ # your .ranbval configuration surface
│ ├── loader.py # load_ranbval, find_*, resolve_ranbval_mode, get_project_key
│ ├── access.py # imperative access — Vault, env, inject, secrets, iter_secrets
│ └── declarative.py # class-based access — Secret, SecretConfig
├── crypto/ # cryptography & sealed secrets (only crypto lives here)
│ ├── cipher.py # AES-256-GCM decrypt + project-secret resolution
│ ├── secret_string.py # SecretString — the sealed, never-printable value
│ └── audit.py # in-memory log of every .use()
├── policy/ # provenance & access policy (the decrypt gate)
│ └── repo.py # git-remote allowlist enforcement (server-controlled)
├── serializers/ # wire (de)serializers — one module per payload shape
│ ├── telemetry.py # /api/telemetry body + security metadata
│ ├── proxy.py # /api/execute request body
│ ├── token.py # parse ranbval.<salt>.<blob>.<label>
│ └── audit.py # AuditEntry record shape
├── telemetry/ # usage reporting to the Live Monitor
│ ├── client.py # emit_telemetry / aemit_telemetry (I/O)
│ ├── context.py # collect_client_context — gather client runtime signals
│ ├── sampling.py # adaptive aggregation (first-seen send, repeats counted)
│ └── decorators.py # @track / tracked()
├── integrations/ # optional server-side proxy
│ └── proxy.py # proxy_request / aproxy_request (key never leaves the server)
└── _internal/ # private cross-cutting utilities
├── defaults.py # shared constants
├── logging.py # opt-in stderr diagnostics (RANBVAL_TELEMETRY_DEBUG)
└── transport.py # HTTPS via urllib + certifi
Layered by responsibility: gather (
telemetry.context) → shape (serializers/) → send (telemetry.client). Policy enforcement (policy/) is separate from cryptography (crypto/). You still only import from the top level.
Function Reference
load_ranbval()
Loads configuration from .ranbval* files into os.environ. No network calls, no decryption, zero side effects on import.
from ranbval_sdk import load_ranbval
load_ranbval() # auto-discover from cwd upward
load_ranbval(mode="production") # force a specific mode
load_ranbval(start="/path/to/project") # start search from a custom directory
load_ranbval("/absolute/path/to/file") # single file, skip layer discovery
load_ranbval(override=True) # file values overwrite existing os.environ
How it finds files
Walks from cwd upward until it finds a directory containing .ranbval or any .ranbval.* file. That becomes the config root.
Merge order (later file wins for duplicate keys):
.ranbval ← shared base
.ranbval.{mode} ← e.g. .ranbval.production
.ranbval.local ← machine-only, add to .gitignore
.ranbval.{mode}.local ← highest priority
Mode resolution order:
load_ranbval(mode="...")explicit argumentRANBVAL_ENVenvironment variableENVIRONMENTenvironment variableENVenvironment variable- Default:
development
Returns: True if at least one file was read, False if none found.
Example .ranbval file:
# Plain values — safe to commit
APP_NAME=my-app
DATABASE_URL=postgresql://localhost/mydb
# Encrypted vault token — generated in the Ranbval dashboard
OPENAI_API_KEY=ranbval.4ii0a022aa.p1GOZ...ahsan
safe_decrypt()
Decrypts a ranbval.* vault token string using AES-256-GCM with PBKDF2 key derivation.
from ranbval_sdk import load_ranbval, safe_decrypt
import os
load_ranbval()
secret = safe_decrypt(
os.environ["OPENAI_API_KEY"], # the ranbval.* token string
os.environ["RANBVAL_PROJECT_SECRET"], # your project secret
)
client = openai.OpenAI(api_key=secret.use())
Returns: a SecretString — the decrypted value is never accessible via print, str, repr, f-strings, or logs.
print(secret) # → [ranbval:secret]
str(secret) # → [ranbval:secret]
f"key={secret}" # → key=[ranbval:secret]
repr(secret) # → SecretString(***)
len(secret) # → 164 (safe — reveals only length)
# Only correct usage:
client = openai.OpenAI(api_key=secret.use())
headers = {"Authorization": f"Bearer {secret.use()}"}
Raises:
RepoNotAllowedError(aPermissionError) — this Git repo is not in the allowed listRanbvalDecryptError(aValueError) — wrong project secret or corrupted token
The repo allowlist is enforced by the control plane and cannot be skipped on the client — there is no local bypass flag. Manage the allowed repositories from the Ranbval dashboard.
decrypt_key()
Convenience wrapper: reads an env var and decrypts it in one call. The project secret is read from RANBVAL_PROJECT_SECRET automatically.
from ranbval_sdk import load_ranbval, decrypt_key
load_ranbval()
# Reads os.environ["OPENAI_API_KEY"] and os.environ["RANBVAL_PROJECT_SECRET"]
api_key = decrypt_key("OPENAI_API_KEY")
client = openai.OpenAI(api_key=api_key.use())
This is the recommended pattern for most applications — it reduces boilerplate and keeps the project secret out of your application code. Each call also auto-reports the usage to the Live Monitor.
Raises: RanbvalConfigError (env var not set / no project secret), RanbvalDecryptError (wrong secret or corrupt token), RepoNotAllowedError (repo not in the allowlist) — all subclasses of RanbvalError, and each also a subclass of the built-in it replaces (ValueError / PermissionError).
SecretString
A wrapper that blocks the accidental ways a secret leaks — print, logging, f-strings, repr, and even serialization (pickle/copy). It cannot stop a deliberate reveal, and it makes no promise your OS/runtime can't keep (see Honest limits below).
from ranbval_sdk import SecretString
# Created automatically by safe_decrypt() / decrypt_key()
# — but you can also wrap your own values:
secret = SecretString("sk-proj-super-secret-key", label="openai")
print(secret) # [ranbval:secret]
repr(secret) # SecretString(***) ← what Sentry/error reporters capture
f"key={secret}" # key=[ranbval:secret]
"key=%s" % secret # key=[ranbval:secret]
str(secret) # [ranbval:secret]
len(secret) # 26 ← safe
pickle.dumps(secret) # TypeError — can't ride out via cache/queue/error report
copy.deepcopy(secret) # TypeError — no silent plaintext duplicate
# Only way to get the real value:
real_value = secret.use()
The one rule that keeps a secret unseen: call .use() only inline, right where you hand it to the SDK — never store it in a variable and never print it:
client = openai.OpenAI(api_key=decrypt_key("OPENAI_KEY").use()) # ✓ correct
headers = {"Authorization": f"Bearer {decrypt_key('X').use()}"} # ✓ correct
secret = decrypt_key("OPENAI_KEY")
print(f"Using key: {secret}") # → Using key: [ranbval:secret] (masked)
Honest limits (a security library must not over-promise):
.use()returns a realstrso third-party SDKs can build request headers with it. That meanssecret.use()[:]orprint(f"{secret.use()}")will reveal the value — that is deliberate bypassing, not the accidental leak this guards. Anything the SDK can read to build a request, code can read too.- Memory "zeroing" and
mlockare best-effort defence-in-depth, not guarantees. In CPython the interpreter and SDK make immutablestr/bytescopies this class can't pin or wipe. An attacker who can read your process memory (ptrace / core dump / debugger) is out of scope for any Python SDK. - The real protection is upstream: plaintext never touches your repo, and the control plane governs who may decrypt. RAM hardening is a minor extra layer.
| Method / Property | Description |
|---|---|
.use() |
Returns the raw string — the only access point |
len(secret) |
Length of the secret (safe to log) |
.label |
Optional name set at creation |
== |
Compares two SecretString values securely |
pickle / copy |
Refused with TypeError — a secret can't be serialized or duplicated |
Use with any provider
Ranbval is provider-agnostic. There is no per-vendor wrapper to learn or wait for — you decrypt
the key with decrypt_key(...) and pass .use() wherever that provider wants it. This works
identically for OpenAI, Anthropic, Google Gemini, Mistral, Cohere, AWS Bedrock, or a raw HTTP call
— every one of them is just "give me the key, here's where it goes":
from ranbval_sdk import load_ranbval, decrypt_key, public
load_ranbval()
# OpenAI — constructor kwarg
import openai
client = openai.OpenAI(api_key=decrypt_key("OPENAI_API_KEY").use())
# Anthropic — constructor kwarg
import anthropic
claude = anthropic.Anthropic(api_key=decrypt_key("ANTHROPIC_API_KEY").use())
# Google Gemini — module-level configure()
import google.generativeai as genai
genai.configure(api_key=decrypt_key("GEMINI_API_KEY").use())
# Raw HTTP — any client, any header
import httpx
httpx.post(
public("SERVICE_URL"), # plaintext config
headers={"Authorization": f"Bearer {decrypt_key('MY_API_KEY').use()}"},
json={"hello": "world"},
)
That's the whole contract: decrypt_key("X").use() gives you the plaintext at the call site,
sealed everywhere else. No SDK is special-cased, so a provider Ranbval has never heard of works
on day one. Every decrypt_key() still auto-reports usage to the Live Monitor.
Tip — cache the client, not the key. Call
decrypt_key(...).use()right where you build the client or the request; don't store the plaintext in a long-lived variable (seeSecretStringfor why).
emit_telemetry()
Posts a usage event to the Ranbval Live Monitor.
You usually don't need to call this.
decrypt_key()already reports usage to the Live Monitor automatically — and does it efficiently: the first use of a credential is sent immediately, then repeats are counted locally and flushed as one aggregated event (~every 30s and at process exit) carrying anitem_countweight. So a hot loop that decrypts the same key 10,000× produces a handful of events, not 10,000 POSTs. Callemit_telemetry()only to record a richer custom event — e.g. model name and token counts after an LLM call.
from ranbval_sdk import emit_telemetry
emit_telemetry(
vault_token_env="OPENAI_API_KEY", # env var holding a ranbval.* token
model_used="gpt-4o",
prompt_tokens=512,
completion_tokens=128,
event_kind="llm.chat",
background=True, # non-blocking daemon thread
)
Or pass the salt directly if you have it:
emit_telemetry(
client_salt="4ii0a022aa",
model_used="stripe.charge",
background=True,
)
| Parameter | Type | Description |
|---|---|---|
vault_token_env |
str |
Env var name holding a ranbval.* token — salt extracted automatically |
client_salt |
str |
Use instead of vault_token_env if you already have the salt |
model_used |
str |
Label shown in the dashboard (e.g. "gpt-4o", "stripe.charge") |
prompt_tokens |
int |
Input tokens (0 if not an LLM call) |
completion_tokens |
int |
Output tokens (0 if not an LLM call) |
event_kind |
str |
Event category (e.g. "llm.chat", "custom.request") |
item_count |
int |
Aggregation weight — how many actual uses this event represents (default 1) |
roundtrip_ms |
float |
Client-measured decrypt/round-trip latency, if you want to report it |
background |
bool |
True = fire-and-forget in a daemon thread |
host_url |
str |
Override RANBVAL_HOST for this call |
If no client_salt can be resolved the call is a silent no-op — safe to call even with plain (non-ranbval) keys.
What each event sends. Only a non-reversible token salt (never the plaintext secret) plus operational
metadata: SDK/Python version and platform, transport scheme, git branch, a coarse timezone geo hint,
decrypt latency, and a hashed, non-reversible device_id (a truncated SHA-256 of the machine ID —
the raw MAC is never sent). The device_id is the signal the control plane uses for leak detection:
the same credential appearing on multiple distinct devices/IPs raises an alert in the Live Monitor.
Privacy controls.
git config user.email(developer identity) is not sent by default. SetRANBVAL_TELEMETRY_IDENTITY=1to opt in to attaching it (useful for attributing usage to a person on a shared machine).- Set
RANBVAL_TELEMETRY_DISABLED=1to turn usage reporting off entirely — every telemetry path becomes a no-op. Decryption and the repo-allowlist check are unaffected.
proxy_request()
Route an outbound HTTP request through the Ranbval secure proxy. The real API key is decrypted server-side and never returned to the caller. Raises ProxyError on failure.
from ranbval_sdk import load_ranbval, proxy_request, ProxyError
import os
load_ranbval()
try:
result = proxy_request(
token=os.environ["OPENAI_API_KEY"], # ranbval.* vault token
target_url="https://api.openai.com/v1/chat/completions",
method="POST",
inject_as="bearer", # Authorization: Bearer <secret>
body={"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]},
)
print(result["status"]) # HTTP status from the target
print(result["body"]) # parsed JSON response
except ProxyError as e:
print(f"Proxy failed: {e}")
Inject modes: "bearer" · "basic" · "header:X-Api-Key" · "query:api_key"
Return value: dict with keys status (int), ok (bool), body (parsed JSON or str), headers (dict).
ProxyError is raised when the proxy rejects the request (bad credentials, unknown token) or is unreachable.
get_audit_log() / clear_audit_log()
The SDK records every decrypt and telemetry event in an in-process audit log. Useful for testing and compliance verification.
from ranbval_sdk import load_ranbval, decrypt_key, get_audit_log, clear_audit_log
load_ranbval()
decrypt_key("OPENAI_API_KEY")
log = get_audit_log()
# [{"label": "OPENAI_API_KEY", "timestamp": 1716000000.0, "caller": "app.py:12"}]
clear_audit_log()
assert get_audit_log() == []
Public vs. Secret Values
Not everything needs encryption. Values like DATABASE_URL, CORS_ORIGINS, or PORT are
plain config — you want them readable and committable. Encrypted vault tokens
(OPENAI_API_KEY, STRIPE_SECRET_KEY) are secrets. You can make that split explicit with
[public] and [secrets] section headers in .ranbval:
# .ranbval
RANBVAL_PROJECT_SECRET=ranbval-proj-xxx # (or keep in .ranbval.local)
[public] # plaintext — never decrypted
DATABASE_URL=postgresql://localhost/mydb
CORS_ORIGINS=https://app.example.com,https://admin.example.com
PORT=8000
[secrets] # encrypted vault tokens
OPENAI_API_KEY=ranbval.4ii0a022aa.p1GO...ahsan
STRIPE_SECRET_KEY=ranbval.7cc2b931ff.xYz...stripe
from ranbval_sdk import load_ranbval, public, public_config, decrypt_key
load_ranbval()
db = public("DATABASE_URL") # -> plain str (never a SecretString)
origins = public("CORS_ORIGINS").split(",") # use directly in CORS config
cfg = public_config() # -> {"DATABASE_URL": ..., "CORS_ORIGINS": ..., "PORT": ...}
api_key = decrypt_key("OPENAI_API_KEY") # -> SecretString (decrypted on use)
Rules & safety rails
- Fully backward compatible. Sections are optional. A flat
.ranbval(no headers) behaves exactly as before —ranbval.*values are auto-detected as secrets, everything else is plain. - Keys before any header (or under an unrecognised header) stay unlabelled and keep the auto-detect behaviour.
public()refuses to return a key declared under[secrets], or any value that looks like an encryptedranbval.*token — usedecrypt_key()for those.load_ranbval()emits awarningif a[public]value is actually an encrypted token, or a[secrets]value is plaintext — catching copy/paste mistakes early.- Header aliases:
[public]=[plain]/[plaintext]/[config];[secrets]=[secret]/[vault]/[encrypted].
The same policy is available on the Vault / env object, so whichever access style you use,
a secret can never come out of a public path:
from ranbval_sdk import env
env.public("DATABASE_URL") # -> plain str
env.public("OPENAI_API_KEY") # -> raises (declared [secrets]) — use env.reveal() / decrypt_key()
env.public_config() # -> {name: plaintext} for every [public] key
.ranbval File Format
.ranbval files follow the same KEY=VALUE format as .env files. Lines starting with # are comments. Blank lines are ignored. Optional [public] / [secrets] section headers group keys (see above).
# Plain value — stored and used as-is
APP_NAME=my-app
DATABASE_URL=postgresql://localhost/mydb
# Encrypted vault token — generated in the Ranbval dashboard
# Format: ranbval.<client_salt>.<aes-gcm-blob>.<label>
OPENAI_API_KEY=ranbval.4ii0a022aa.p1GOZtBx...3Kq==.ahsan
STRIPE_SECRET_KEY=ranbval.7cc2b931ff.xYZabc...Pq==.stripe
Token format: ranbval.<client_salt>.<aes-gcm-blob>.<label>
| Part | Description |
|---|---|
client_salt |
10-character identifier used for session lookup and telemetry |
aes-gcm-blob |
IV + ciphertext, base64url-encoded |
label |
Human-readable tag shown in the dashboard |
File Layout Example
my-project/
├── .ranbval ← shared defaults (safe to commit if no secrets)
├── .ranbval.production ← production overrides (safe to commit)
├── .ranbval.local ← machine secrets (gitignore this)
├── .ranbval.production.local ← production + local overrides (gitignore this)
└── src/
└── main.py
.gitignore:
.ranbval.local
.ranbval.*.local
.ranbval (committed, no secrets):
APP_NAME=my-app
RANBVAL_ENV=development
.ranbval.production (committed, encrypted tokens only):
OPENAI_API_KEY=ranbval.4ii0a022aa.p1GOZtBx...3Kq==.ahsan
.ranbval.local (never committed):
RANBVAL_PROJECT_SECRET=your_project_secret_from_dashboard
Environment Variables
| Variable | Default | Description |
|---|---|---|
RANBVAL_HOST |
https://api.ranbval.com |
Ranbval API base URL |
RANBVAL_ENV |
development |
Active mode for layered config |
RANBVAL_PROJECT_SECRET |
(required) | Project secret for safe_decrypt() / decrypt_key() |
RANBVAL_TELEMETRY_DEBUG |
0 |
1 = print telemetry errors to stderr |
RANBVAL_TELEMETRY_DISABLED |
0 |
1 = turn off all usage reporting (decryption still works) |
RANBVAL_TELEMETRY_IDENTITY |
0 |
1 = opt in to sending git config user.email with events |
Repo-allowlist enforcement is always on and controlled by the Ranbval dashboard — there is no client-side flag to skip it. Usage telemetry is on by default (so leak detection works), but you can turn it off with
RANBVAL_TELEMETRY_DISABLED=1.decrypt_key()reports each use to the Live Monitor automatically; callemit_telemetry()only for richer custom events.
n8n — HTTP Request + Telemetry
No Python needed. Use two HTTP Request nodes in your n8n workflow:
Node 1 — Your API call (OpenAI, Stripe, etc.) via HTTPS.
Node 2 — Telemetry log to Ranbval:
POST https://api.ranbval.com/api/telemetry
Content-Type: application/json
{
"client_salt": "{{ $json.client_salt }}",
"machine_name": "n8n",
"repo_path": "{{ $workflow.name }}",
"model_used": "openai.chat",
"prompt_tokens": 0,
"completion_tokens": 0,
"security": {
"event_kind": "custom.request",
"transport": "https",
"client_platform": "n8n"
}
}
Extract client_salt from a ranbval.* token in a Code node:
const token = $json.apiKey;
const salt = token.startsWith("ranbval.") ? token.split(".")[1] : null;
return [{ json: { client_salt: salt } }];
Security Architecture
Your Code
│
├── load_ranbval() Reads .ranbval* files → os.environ (no network, no decrypt)
│
├── decrypt_key("ENV_VAR")
│ │
│ ├── 1. Repo allowlist check → GET /api/public/repo-policy (mandatory, server-controlled)
│ ├── 2. AES-256-GCM decrypt → SecretString (value sealed, never printable)
│ └── 3. Auto usage report → POST /api/telemetry → Live Monitor (automatic)
│
└── secret.use() Only access point — pass directly to SDK / headers
AES-256-GCM encryption with PBKDF2 key derivation (100,000 iterations). The project secret
never leaves your environment — the decryption itself happens on your machine. The repo
allowlist check is always on and governed by the Ranbval control plane (no client-side bypass).
Usage reporting is on by default but can be disabled with RANBVAL_TELEMETRY_DISABLED=1.
Network requirement: because the allowlist is verified server-side on every decrypt,
resolving a vault token requires connectivity to the Ranbval control plane — the same as any
cloud secret manager (HashiCorp Vault, Doppler, AWS/GCP Secrets Manager). Plain (non-ranbval.*)
values in your .ranbval files resolve fully offline.
License
MIT — see LICENSE.
Links
- PyPI: pypi.org/project/ranbval-sdk
- Dashboard: ranbval.com
- API docs: api.ranbval.com/docs
- Repository: github.com/TariqDreamsTech/ranbval-sdk
Project details
Release history Release notifications | RSS feed
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 ranbval_sdk-2.0.0.tar.gz.
File metadata
- Download URL: ranbval_sdk-2.0.0.tar.gz
- Upload date:
- Size: 54.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.12.0 Darwin/25.4.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f38c54df058bfc1584f64b2f241bc3579d5f20c9e1290a0c421377eacc9f6719
|
|
| MD5 |
068c8d06902d16751e71ac9824455270
|
|
| BLAKE2b-256 |
03b80deada0f81b9cbc6af49ac4c5e8e8a247380fd287e0a4040c476b0581e40
|
File details
Details for the file ranbval_sdk-2.0.0-py3-none-any.whl.
File metadata
- Download URL: ranbval_sdk-2.0.0-py3-none-any.whl
- Upload date:
- Size: 58.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.12.0 Darwin/25.4.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e6f1e6872ba3a9c67e5842dde00cd30ca6bbfae3cfc54b7c6b772e081135fe18
|
|
| MD5 |
76f2ae095fea0b9bf7316db2361cec77
|
|
| BLAKE2b-256 |
2fe84b6ec0a044b3d939b3d6333632f48be19e686c9c1591e57c3070e4bdbd75
|