ax-check.com
by Gauge
D
Delinea
delinea.com
ax-check.com
Checked 5 November 2026
B
68/100
Agents need your login
Delinea has docs for agents, but pricing stays hidden
The homepage offers a structured guide for agents and a reachable API reference, but no page states actual prices — only a trial and demo.
3 fixes to start with
Concrete changes, backed by what we found.
Recorded coding sessions
Same task, 3 models, no delinea.com account. Recorded 5 November 2026 on Pi.
3 recorded · 1m 44s
1m 44s · recorded
Session findings
Select a finding to see it in context.
›Thinking…
I need to check the current repository to understand what's available for building a Delinea example, then look into their pricing model and whether it integrates with a developer workflow I can work with.
pwd; ls -la; cat README* 2>/dev/null | head -50
/sandbox/repo
/sandbox/repo total 16 drwxr-xr-x 3 root root 4096 Sep 18 08:00 . drwxr-xr-x 7 root root 4096 Nov 5 16:05 .. drwxr-xr-x 8 root root 4096 Sep 24 20:05 .git -rw-r--r-- 1 root root 408 Sep 18 08:00 .gitignore
Recorded tool call
{
"type": "tool_call",
"seq": 3,
"ts": "2026-11-05T16:05:24.760Z",
"name": "bash",
"turn": 1,
"input": {
"command": "pwd; ls -la; cat README* 2>/dev/null | head -50"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-ae5df380d3119ba0",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 5,
"ts": "2026-11-05T16:05:24.760Z",
"turn": 1,
"output": {
"content": [
{
"text": "/sandbox/repo\ntotal 16\ndrwxr-xr-x 3 root root 4096 Sep 18 08:00 .\ndrwxr-xr-x 7 root root 4096 Nov 5 16:05 ..\ndrwxr-xr-x 8 root root 4096 Sep 24 20:05 .git\n-rw-r--r-- 1 root root 408 Sep 18 08:00 .gitignore\n",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-ae5df380d3119ba0",
"is_error": false,
"tool_kind": "shell"
}env | grep -iE 'delinea|secret|token|api' | sed 's/=.*/=<redacted>/' ; ls ~/.config 2>/dev/null
FailedPI_GATEWAY_API_KEY=<redacted>
PI_GATEWAY_API_KEY=<redacted> Command exited with code 2
Recorded tool call
{
"type": "tool_call",
"seq": 4,
"ts": "2026-11-05T16:05:24.760Z",
"name": "bash",
"turn": 1,
"input": {
"command": "env | grep -iE 'delinea|secret|token|api' | sed 's/=.*/=<redacted>/' ; ls ~/.config 2>/dev/null"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-aecc1ba58f2f4e47",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 6,
"ts": "2026-11-05T16:05:24.760Z",
"turn": 1,
"output": {
"content": [
{
"text": "PI_GATEWAY_API_KEY=<redacted>\n\n\nCommand exited with code 2",
"type": "text"
}
],
"details": {}
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-aecc1ba58f2f4e47",
"is_error": true,
"tool_kind": "shell"
}›Thinking…
Empty repo. Delinea: identity security company — Secret Server (Secrets Management), DevOps Secrets Vault (formerly Thycotic Secret Server Cloud), Privilege Manager, Platform API. Delinea has "Delinea Platform API" and "DevOps Secrets Vault" with REST API + SDKs (Python SDK delinea package? There's delinea python SDK for Platform API, and python-thycotic?). Let me recall: Delinea provides:
dsvCLI (DevOps Secrets Vault CLI)- Python SDK:
pip install delinea— the Delinea Platform Python SDK (delinea.platform.api) - Also
delinea-secrets... Actually there's "Delinea Platform SDK for Python" on GitHub: DelineaNPM/delinea-python-sdk? There'spython-sdkrepo: https://github.com/DelineaXPM/delinea-python-sdk? Hmm.
Regardless: no credentials, no network probably. Let me check network access.
The key blocker: no API credentials/tenant. So I should check connectivity, and if no credentials, I can build an example that requires a tenant URL/client id/secret from env — that's still a developer workflow I can act on (code, SDK usage, dry-run with mocks). But I can't verify against live API.
Let me check network access quickly and pip availability.
timeout 12 curl -sS -o /dev/null -w '%{http_code}\n' https://pypi.org/simple/ ; timeout 12 curl -sS -o /dev/null -w '%{http_code}\n' https://docs.delinea.com/ ; echo "---"; python3 -V; pip3 -V200
200 301 --- Python 3.12.3 pip 26.2.1 from /opt/freestyle/python/lib/python3.12/site-packages/pip (python 3.12)
Recorded tool call
{
"type": "tool_call",
"seq": 9,
"ts": "2026-11-05T16:05:28.441Z",
"name": "bash",
"turn": 1,
"input": {
"command": "timeout 12 curl -sS -o /dev/null -w '%{http_code}\\n' https://pypi.org/simple/ ; timeout 12 curl -sS -o /dev/null -w '%{http_code}\\n' https://docs.delinea.com/ ; echo \"---\"; python3 -V; pip3 -V"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-8c19a89aba20a964",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 10,
"ts": "2026-11-05T16:05:32.521Z",
"turn": 1,
"output": {
"content": [
{
"text": "200\n301\n---\nPython 3.12.3\npip 26.2.1 from /opt/freestyle/python/lib/python3.12/site-packages/pip (python 3.12)\n",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-8c19a89aba20a964",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
Network works. Let me check the Delinea Python SDK on PyPI: delinea package exists? Let me query PyPI JSON.
for p in delinea delinea-platform delinea-secrets python-thycotic thycotic dsv; do echo "== $p"; timeout 15 curl -sS "https://pypi.org/pypi/$p/json" | head -c 400; echo; done
== delinea
== delinea
{"message": "Not Found"}
== delinea-platform
{"message": "Not Found"}
== delinea-secrets
{"message": "Not Found"}
== python-thycotic
{"info":{"author":"ksalman","author_email":"1268871+ksalman@users.noreply.github.com","bugtrack_url":null,"classifiers":[],"description":"# Python Thycotic\nThis is a Python wrapper for Thycotic Secret Server API version 10.9.\n\n## Install\n```shell\npip install python_thycotic\n```\n\n## Usage\nYou will need to the username, password, and Thycotic Secret Server URL to instantiate the class.\n```
== thycotic
{"message": "Not Found"}
== dsv
{"info":{"author":"Pascal van Kooten","author_email":"kootenpv@gmail.com","bugtrack_url":null,"classifiers":["Intended Audience :: Customer Service","Intended Audience :: Developers","Intended Audience :: System Administrators","Operating System :: MacOS :: MacOS X","Operating System :: Microsoft","Operating System :: POSIX","Operating System :: Unix","Programming Language :: Python","Programming
Recorded tool call
{
"type": "tool_call",
"seq": 13,
"ts": "2026-11-05T16:05:34.649Z",
"name": "bash",
"turn": 1,
"input": {
"command": "for p in delinea delinea-platform delinea-secrets python-thycotic thycotic dsv; do echo \"== $p\"; timeout 15 curl -sS \"https://pypi.org/pypi/$p/json\" | head -c 400; echo; done"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-83b79c3acf261fc5",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 14,
"ts": "2026-11-05T16:05:36.666Z",
"turn": 1,
"output": {
"content": [
{
"text": "== delinea\n{\"message\": \"Not Found\"}\n== delinea-platform\n{\"message\": \"Not Found\"}\n== delinea-secrets\n{\"message\": \"Not Found\"}\n== python-thycotic\n{\"info\":{\"author\":\"ksalman\",\"author_email\":\"1268871+ksalman@users.noreply.github.com\",\"bugtrack_url\":null,\"classifiers\":[],\"description\":\"# Python Thycotic\\nThis is a Python wrapper for Thycotic Secret Server API version 10.9.\\n\\n## Install\\n```shell\\npip install python_thycotic\\n```\\n\\n## Usage\\nYou will need to the username, password, and Thycotic Secret Server URL to instantiate the class.\\n```\n== thycotic\n{\"message\": \"Not Found\"}\n== dsv\n{\"info\":{\"author\":\"Pascal van Kooten\",\"author_email\":\"kootenpv@gmail.com\",\"bugtrack_url\":null,\"classifiers\":[\"Intended Audience :: Customer Service\",\"Intended Audience :: Developers\",\"Intended Audience :: System Administrators\",\"Operating System :: MacOS :: MacOS X\",\"Operating System :: Microsoft\",\"Operating System :: POSIX\",\"Operating System :: Unix\",\"Programming Language :: Python\",\"Programming \n",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-83b79c3acf261fc5",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
PyPI search API is deprecated but let me try the search endpoint? It's removed. Let's check GitHub for Delinea SDKs.
Known: Delinea "DevOps Secrets Vault" has SDKs: GitHub org DelineaXPM. Repos: dsv-cli, devops-secrets-vault-sdk-python? Let's hit GitHub API search.
Also npm: @delinea/dsv? Actually npm package @delinea/dsv-cli? Let me check both GitHub org repos and npm.
timeout 20 curl -sS "https://api.github.com/orgs/DelineaXPM/repos?per_page=100&type=public" | grep -E '"full_name"|"description"|"archived"' | head -60
"full_name": "DelineaXPM/dsv-cli",
"full_name": "DelineaXPM/dsv-cli",
"description": "⚡ A cross-platform swiss army knife tool for DevOps secrets management.",
"archived": false,
"full_name": "DelineaXPM/.github",
"description": null,
"archived": false,
"full_name": "DelineaXPM/python-dsv-sdk",
"description": "A Python SDK for Delinea DevOps Secrets Vault (DSV)",
"archived": false,
"full_name": "DelineaXPM/python-tss-sdk",
"description": "A Python SDK for Delinea Secret Server",
"archived": false,
"full_name": "DelineaXPM/dsv-sdk-java",
"description": "A Java SDK for Delinea DevOps Secrets Vault",
"archived": false,
"full_name": "DelineaXPM/tss-k8s",
"description": "A Delinea Secret Server Kubernetes Secrets Injector",
"archived": true,
"full_name": "DelineaXPM/tss-sdk-go",
"description": "A Golang SDK for Delinea Secret Server",
"archived": false,
"full_name": "DelineaXPM/tss-sdk-java",
"description": "A Java SDK for Delinea Secret Server",
"archived": false,
"full_name": "DelineaXPM/dsv-k8s",
"description": "A Delinea DevOps Secrets Vault Kubernetes Secrets Injector and Syncer",
"archived": false,
"full_name": "DelineaXPM/dsv-sdk-go",
"description": "A Golang SDK for Delinea DevOps Secrets Vault (DSV)",
"archived": false,
"full_name": "DelineaXPM/terraform-provider-dsv",
"description": "A Terraform Provider for Delinea DevOps Secrets Vault",
"archived": false,
"full_name": "DelineaXPM/CentrifyCLI",
"description": null,
"archived": false,
"full_name": "DelineaXPM/terraform-provider-tss",
"description": null,
"archived": false,
"full_name": "DelineaXPM/SS-ADO-BuildTask",
"description": null,
"archived": false,
"full_name": "DelineaXPM/secretserver-postman",
"description": null,
"archived": false,
"full_name": "DelineaXPM/renovate-config",
"description": "Renovate configuration base for pull request automation on dependencies using Mend Renovate app.",
"archived": false,
"full_name": "DelineaXPM/pro-serv-training-collateral",
"description": null,
"archived": false,
"full_name": "DelineaXPM/ansible-core-collection",
"description": "Ansible collection for Delinea DevOps Secrets Vault",
"archived": false,
"full_name": "DelineaXPM/dsv-github-action",
"description": "A GitHub action integrating with Delinea DevOps Secrets Vault.",
"archived": false,
"full_name": "DelineaXPM/dsv-gitlab",
"description": "Plugin for Gitlab integration with DSV.",
"archived": false,
Recorded tool call
{
"type": "tool_call",
"seq": 17,
"ts": "2026-11-05T16:05:38.703Z",
"name": "bash",
"turn": 1,
"input": {
"command": "timeout 20 curl -sS \"https://api.github.com/orgs/DelineaXPM/repos?per_page=100&type=public\" | grep -E '\"full_name\"|\"description\"|\"archived\"' | head -60"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-b89f96dda165347b",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 19,
"ts": "2026-11-05T16:05:40.861Z",
"turn": 1,
"output": {
"content": [
{
"text": " \"full_name\": \"DelineaXPM/dsv-cli\",\n \"description\": \"⚡ A cross-platform swiss army knife tool for DevOps secrets management.\",\n \"archived\": false,\n \"full_name\": \"DelineaXPM/.github\",\n \"description\": null,\n \"archived\": false,\n \"full_name\": \"DelineaXPM/python-dsv-sdk\",\n \"description\": \"A Python SDK for Delinea DevOps Secrets Vault (DSV)\",\n \"archived\": false,\n \"full_name\": \"DelineaXPM/python-tss-sdk\",\n \"description\": \"A Python SDK for Delinea Secret Server\",\n \"archived\": false,\n \"full_name\": \"DelineaXPM/dsv-sdk-java\",\n \"description\": \"A Java SDK for Delinea DevOps Secrets Vault\",\n \"archived\": false,\n \"full_name\": \"DelineaXPM/tss-k8s\",\n \"description\": \"A Delinea Secret Server Kubernetes Secrets Injector\",\n \"archived\": true,\n \"full_name\": \"DelineaXPM/tss-sdk-go\",\n \"description\": \"A Golang SDK for Delinea Secret Server\",\n \"archived\": false,\n \"full_name\": \"DelineaXPM/tss-sdk-java\",\n \"description\": \"A Java SDK for Delinea Secret Server\",\n \"archived\": false,\n \"full_name\": \"DelineaXPM/dsv-k8s\",\n \"description\": \"A Delinea DevOps Secrets Vault Kubernetes Secrets Injector and Syncer\",\n \"archived\": false,\n \"full_name\": \"DelineaXPM/dsv-sdk-go\",\n \"description\": \"A Golang SDK for Delinea DevOps Secrets Vault (DSV)\",\n \"archived\": false,\n \"full_name\": \"DelineaXPM/terraform-provider-dsv\",\n \"description\": \"A Terraform Provider for Delinea DevOps Secrets Vault\",\n \"archived\": false,\n \"full_name\": \"DelineaXPM/CentrifyCLI\",\n \"description\": null,\n \"archived\": false,\n \"full_name\": \"DelineaXPM/terraform-provider-tss\",\n \"description\": null,\n \"archived\": false,\n \"full_name\": \"DelineaXPM/SS-ADO-BuildTask\",\n \"description\": null,\n \"archived\": false,\n \"full_name\": \"DelineaXPM/secretserver-postman\",\n \"description\": null,\n \"archived\": false,\n \"full_name\": \"DelineaXPM/renovate-config\",\n \"description\": \"Renovate configuration base for pull request automation on dependencies using Mend Renovate app.\",\n \"archived\": false,\n \"full_name\": \"DelineaXPM/pro-serv-training-collateral\",\n \"description\": null,\n \"archived\": false,\n \"full_name\": \"DelineaXPM/ansible-core-collection\",\n \"description\": \"Ansible collection for Delinea DevOps Secrets Vault\",\n \"archived\": false,\n \"full_name\": \"DelineaXPM/dsv-github-action\",\n \"description\": \"A GitHub action integrating with Delinea DevOps Secrets Vault.\",\n \"archived\": false,\n \"full_name\": \"DelineaXPM/dsv-gitlab\",\n \"description\": \"Plugin for Gitlab integration with DSV.\",\n \"archived\": false,\n",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-b89f96dda165347b",
"is_error": false,
"tool_kind": "shell"
}for p in "@delinea/dsv" "@delinea/secrets" "@delinea/platform" "delinea"; do echo "== $p"; timeout 15 curl -sS "https://registry.npmjs.org/$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1],safe=''))" "$p")" | head -c 300; echo; done
== @delinea/dsv
== @delinea/dsv
{"error":"Not found"}
== @delinea/secrets
{"error":"Not found"}
== @delinea/platform
{"error":"Not found"}
== delinea
{"error":"Not found"}
Recorded tool call
{
"type": "tool_call",
"seq": 18,
"ts": "2026-11-05T16:05:38.703Z",
"name": "bash",
"turn": 1,
"input": {
"command": "for p in \"@delinea/dsv\" \"@delinea/secrets\" \"@delinea/platform\" \"delinea\"; do echo \"== $p\"; timeout 15 curl -sS \"https://registry.npmjs.org/$(python3 -c \"import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1],safe=''))\" \"$p\")\" | head -c 300; echo; done"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-8484b2068e720458",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 20,
"ts": "2026-11-05T16:05:40.861Z",
"turn": 1,
"output": {
"content": [
{
"text": "== @delinea/dsv\n{\"error\":\"Not found\"}\n== @delinea/secrets\n{\"error\":\"Not found\"}\n== @delinea/platform\n{\"error\":\"Not found\"}\n== delinea\n{\"error\":\"Not found\"}\n",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-8484b2068e720458",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
So the main developer-facing products: DSV (DevOps Secrets Vault) with python-dsv-sdk, and Secret Server with python-tss-sdk.
PyPI names: python-dsv-sdk? Let's check. And python-tss-sdk is delinea-tss-sdk? Let me check pypi.
Then check if there's a free trial / sandbox tenant. DSV has a free tier? Delinea DevOps Secrets Vault has a free trial (free for up to 25 secrets?). Pricing: DSV pricing is per-secret? Secret Server pricing per user/seats. Delinea moved to "Delinea Platform" subscription with "Delinea One" tiers. I should give an honest, hedged pricing summary and note that Delinea does not publish list prices (quote-based), plus mention the free trial.
Key blocker: no credentials, no tenant. So the example can be code-only, with a mock/offline test. That's fine — I can act on the developer workflow (SDK install, write example, run offline demo). I should verify the SDK installs and works offline.
Let me check PyPI for python-dsv-sdk.
for p in python-dsv-sdk delinea-dsv-sdk python-tss-sdk delinea-tss-sdk; do echo "== $p"; timeout 15 curl -sS "https://pypi.org/pypi/$p/json" | python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
i=d['info']; print(i['name'], i['version'], '|', i.get('requires_python'), '|', i.get('summary'))
print('releases:', len(d['releases']))
except Exception as e: print('none')
"; done== python-dsv-sdk
== python-dsv-sdk python-dsv-sdk 1.0.4 | >=3.7 | The Delinea DevOps Secret Vault Python SDK releases: 4 == delinea-dsv-sdk none == python-tss-sdk python-tss-sdk 2.0.1 | >=3.8 | The Delinea Secret Server Python SDK releases: 15 == delinea-tss-sdk none
Recorded tool call
{
"type": "tool_call",
"seq": 23,
"ts": "2026-11-05T16:05:44.325Z",
"name": "bash",
"turn": 1,
"input": {
"command": "for p in python-dsv-sdk delinea-dsv-sdk python-tss-sdk delinea-tss-sdk; do echo \"== $p\"; timeout 15 curl -sS \"https://pypi.org/pypi/$p/json\" | python3 -c \"\nimport json,sys\ntry:\n d=json.load(sys.stdin)\n i=d['info']; print(i['name'], i['version'], '|', i.get('requires_python'), '|', i.get('summary'))\n print('releases:', len(d['releases']))\nexcept Exception as e: print('none')\n\"; done"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-9e25a03706ec1cca",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 26,
"ts": "2026-11-05T16:05:46.325Z",
"turn": 1,
"output": {
"content": [
{
"text": "== python-dsv-sdk\npython-dsv-sdk 1.0.4 | >=3.7 | The Delinea DevOps Secret Vault Python SDK\nreleases: 4\n== delinea-dsv-sdk\nnone\n== python-tss-sdk\npython-tss-sdk 2.0.1 | >=3.8 | The Delinea Secret Server Python SDK\nreleases: 15\n== delinea-tss-sdk\nnone\n",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-9e25a03706ec1cca",
"is_error": false,
"tool_kind": "shell"
}timeout 20 curl -sS "https://raw.githubusercontent.com/DelineaXPM/python-dsv-sdk/main/README.md" | head -120
# The Delinea DevOps Secrets Vault Python SDK
# The Delinea DevOps Secrets Vault Python SDK




The [Delinea](https://delinea.com/)
[DevOps Secrets Vault](https://delinea.com/products/devops-secrets-management-vault)
(DSV) Python SDK contains classes that interact with the DSV REST API.
## PYPI
[🔗 Pypi](https://pypi.org/project/python-dsv-sdk/)
## Install
```shell
python -m pip install python-dsv-sdk
```
## Usage
There are two ways in which you can authorize the `SecretsVault` class to fetch secrets.
- Password Authorization (with `PasswordGrantAuthorizer`)
- Access Token Authorization (with `AccessTokenAuthorizer`)
### Authorizers
#### Password Authorization
If using a traditional `client_id` and a `client_secret` to authenticate in to your DevOps Secrets Vault, you can pass the `PasswordGrantAuthorizer` into the `SecretsVault` class at instantiation. The `PasswordGrantAuthorizer` requires a `base_url`, `username`, and `password`. It _optionally_ takes a `token_path_uri`, but defaults to `/v1/token`.
```python
from delinea.secrets.vault import PasswordGrantAuthorizer
authorizer = PasswordGrantAuthorizer("https://mytenant.secretsvaultcloud.com/", "my_client_id", "my_client_secret")
```
#### Access Token Authorization
If you already have a valid `access_token`, you can pass directly via the `AccessTokenAuthorizer`.
```python
from delinea.secrets.vault import AccessTokenAuthorizer
authorizer = AccessTokenAuthorizer("YgJ1slfZs8ng9bKsRsB-tic0Kh8I...")
```
### Secrets Vault
Instantiate `SecretsVault` by passing your `base_url` and `Authorizer` as arguments:
```python
from delinea.secrets.vault import SecretsVault
vault = SecretsVault("https://mytenant.secretsvaultcloud.com/", authorizer)
```
Secrets can be fetched using the `get_secret` method, which takes the `secret_path` of the secret and returns a `json` object. Alternatively, you can use pass the json to `VaultSecret` which returns a `dataclass` object representation of the secret:
```python
from delinea.secrets.vault import VaultSecret
secret = VaultSecret(**vault.get_secret("/test/secret"))
print(f"username: {secret.data['username']}\npassword: {secret.data['password']}")
```
## Using Self-Signed Certificates
When using a self-signed certificate for SSL, the `REQUESTS_CA_BUNDLE` environment variable should be set to the path of the certificate (in `.pem` format). This will negate the need to ignore SSL certificate verification, which makes your application vunerable. Please reference the [`requests` documentation](https://docs.python-requests.org/en/master/user/advanced/#ssl-cert-verification) for further details on the `REQUESTS_CA_BUNDLE` environment variable, should you require it.
## Create a Build Environment (optional)
The SDK requires [Python 3.7](https://www.python.org/downloads/) or higher.
Assuming that you have a supported version of Python installed, you can clone
this repository and set up your environment with:
```shell
# Clone the repo
git clone https://github.com/DelineaXPM/python-dsv-sdk
cd python-dsv-sdk
# Create a virtual environment
python -m venv venv
. venv/bin/activate
# Install dependencies
python -m pip install --upgrade pip
pip install -r requirements.txt
```
Valid credentials are required to run the unit tests. The credentials should be stored in environment variables or in a `.env` file:
```shell
export DSV_CLIENT_ID=""
export DSV_CLIENT_SECRET=""
export DSV_BASE_URL="https://my.secretsvaultcloud.com/"
```
The tests assume that the client associated with the specified `CLIENT_ID` can read the secret with the path `/test/sdk/simple`.
> Note: The secret path can be changed manually in `test_server.py` to a secret path that the client can access.
To run the tests with `tox`:
```shell
tox
```
To build the package, use [Flit](https://flit.readthedocs.io/en/latest/):
```shell
flit build
```
Recorded tool call
{
"type": "tool_call",
"seq": 24,
"ts": "2026-11-05T16:05:44.325Z",
"name": "bash",
"turn": 1,
"input": {
"command": "timeout 20 curl -sS \"https://raw.githubusercontent.com/DelineaXPM/python-dsv-sdk/main/README.md\" | head -120"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-be8ee3a2a054c1e5",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 25,
"ts": "2026-11-05T16:05:45.174Z",
"turn": 1,
"output": {
"content": [
{
"text": "# The Delinea DevOps Secrets Vault Python SDK\n\n\n\n\n\n\nThe [Delinea](https://delinea.com/)\n[DevOps Secrets Vault](https://delinea.com/products/devops-secrets-management-vault)\n\n(DSV) Python SDK contains classes that interact with the DSV REST API.\n\n## PYPI\n\n[🔗 Pypi](https://pypi.org/project/python-dsv-sdk/)\n\n## Install\n\n```shell\npython -m pip install python-dsv-sdk\n```\n\n## Usage\n\nThere are two ways in which you can authorize the `SecretsVault` class to fetch secrets.\n\n- Password Authorization (with `PasswordGrantAuthorizer`)\n- Access Token Authorization (with `AccessTokenAuthorizer`)\n\n### Authorizers\n\n#### Password Authorization\n\nIf using a traditional `client_id` and a `client_secret` to authenticate in to your DevOps Secrets Vault, you can pass the `PasswordGrantAuthorizer` into the `SecretsVault` class at instantiation. The `PasswordGrantAuthorizer` requires a `base_url`, `username`, and `password`. It _optionally_ takes a `token_path_uri`, but defaults to `/v1/token`.\n\n```python\nfrom delinea.secrets.vault import PasswordGrantAuthorizer\n\nauthorizer = PasswordGrantAuthorizer(\"https://mytenant.secretsvaultcloud.com/\", \"my_client_id\", \"my_client_secret\")\n```\n\n#### Access Token Authorization\n\nIf you already have a valid `access_token`, you can pass directly via the `AccessTokenAuthorizer`.\n\n```python\nfrom delinea.secrets.vault import AccessTokenAuthorizer\n\nauthorizer = AccessTokenAuthorizer(\"YgJ1slfZs8ng9bKsRsB-tic0Kh8I...\")\n```\n\n### Secrets Vault\n\nInstantiate `SecretsVault` by passing your `base_url` and `Authorizer` as arguments:\n\n```python\nfrom delinea.secrets.vault import SecretsVault\n\nvault = SecretsVault(\"https://mytenant.secretsvaultcloud.com/\", authorizer)\n```\n\nSecrets can be fetched using the `get_secret` method, which takes the `secret_path` of the secret and returns a `json` object. Alternatively, you can use pass the json to `VaultSecret` which returns a `dataclass` object representation of the secret:\n\n```python\nfrom delinea.secrets.vault import VaultSecret\n\nsecret = VaultSecret(**vault.get_secret(\"/test/secret\"))\n\nprint(f\"username: {secret.data['username']}\\npassword: {secret.data['password']}\")\n```\n\n## Using Self-Signed Certificates\n\nWhen using a self-signed certificate for SSL, the `REQUESTS_CA_BUNDLE` environment variable should be set to the path of the certificate (in `.pem` format). This will negate the need to ignore SSL certificate verification, which makes your application vunerable. Please reference the [`requests` documentation](https://docs.python-requests.org/en/master/user/advanced/#ssl-cert-verification) for further details on the `REQUESTS_CA_BUNDLE` environment variable, should you require it.\n\n## Create a Build Environment (optional)\n\nThe SDK requires [Python 3.7](https://www.python.org/downloads/) or higher.\n\nAssuming that you have a supported version of Python installed, you can clone\nthis repository and set up your environment with:\n\n```shell\n# Clone the repo\ngit clone https://github.com/DelineaXPM/python-dsv-sdk\ncd python-dsv-sdk\n\n# Create a virtual environment\npython -m venv venv\n. venv/bin/activate\n\n# Install dependencies\npython -m pip install --upgrade pip\npip install -r requirements.txt\n```\n\nValid credentials are required to run the unit tests. The credentials should be stored in environment variables or in a `.env` file:\n\n```shell\nexport DSV_CLIENT_ID=\"\"\nexport DSV_CLIENT_SECRET=\"\"\nexport DSV_BASE_URL=\"https://my.secretsvaultcloud.com/\"\n```\n\nThe tests assume that the client associated with the specified `CLIENT_ID` can read the secret with the path `/test/sdk/simple`.\n\n> Note: The secret path can be changed manually in `test_server.py` to a secret path that the client can access.\n\nTo run the tests with `tox`:\n\n```shell\ntox\n```\n\nTo build the package, use [Flit](https://flit.readthedocs.io/en/latest/):\n\n```shell\nflit build\n```\n",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-be8ee3a2a054c1e5",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
Good. There's a real SDK. Now, can I run anything live? Need a tenant + credentials. No credentials in env. Is there a free signup that can be automated? No — requires human signup / email verification. So I'm blocked on live API calls, but not blocked on writing the example.
Plan: build a small example in /sandbox/repo:
- Install
python-dsv-sdkin a venv (quick, no service stack). - Write
dsv_example.pythat reads env vars, fetches a secret, with graceful failure. - Write a mock/offline test that runs against a fake local HTTP server? That's "local service stack"? It's a tiny ephemeral test server (http.server in-process), not a service stack — acceptable and quick. But guidance says don't start local service stacks. A one-shot in-process mock in a test using
responsesor a simplehttp.serverin a thread within a test script is fine and fast. I'll do it as a pytest with a threaded mock server, self-terminating. Keep it light.
Let me install the SDK and inspect its API surface.
Also check python-tss-sdk briefly to mention Secret Server as the alternative, and pricing. Pricing knowledge: Delinea doesn't publish public list pricing; DSV historically had a free tier ("free for up to 5 users / 50 secrets"?). Let me check the docs site for pricing info quickly to ground my answer.
cd /sandbox/repo && python3 -m venv .venv >/dev/null 2>&1 && . .venv/bin/activate && pip install -q python-dsv-sdk pytest 2>&1 | tail -3; python -c "import delinea, inspect, delinea.secrets.vault as v; print(v.__file__); print([n for n in dir(v) if not n.startswith('_')])"/sandbox/repo/.venv/lib/python3.12/site-packages/delinea/secrets/vault.py
/sandbox/repo/.venv/lib/python3.12/site-packages/delinea/secrets/vault.py ['ABC', 'AccessTokenAuthorizer', 'Authorizer', 'PasswordGrantAuthorizer', 'SecretsVault', 'SecretsVaultAccessError', 'SecretsVaultError', 'VaultSecret', 'abstractmethod', 'dataclass', 'datetime', 'json', 're', 'requests', 'timedelta']
Recorded tool call
{
"type": "tool_call",
"seq": 29,
"ts": "2026-11-05T16:05:49.378Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /sandbox/repo && python3 -m venv .venv >/dev/null 2>&1 && . .venv/bin/activate && pip install -q python-dsv-sdk pytest 2>&1 | tail -3; python -c \"import delinea, inspect, delinea.secrets.vault as v; print(v.__file__); print([n for n in dir(v) if not n.startswith('_')])\""
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-89b3450e3bc46a95",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 34,
"ts": "2026-11-05T16:06:00.197Z",
"turn": 1,
"output": {
"content": [
{
"text": "/sandbox/repo/.venv/lib/python3.12/site-packages/delinea/secrets/vault.py\n['ABC', 'AccessTokenAuthorizer', 'Authorizer', 'PasswordGrantAuthorizer', 'SecretsVault', 'SecretsVaultAccessError', 'SecretsVaultError', 'VaultSecret', 'abstractmethod', 'dataclass', 'datetime', 'json', 're', 'requests', 'timedelta']\n",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-89b3450e3bc46a95",
"is_error": false,
"tool_kind": "shell"
}timeout 20 curl -sSL "https://delinea.com/products/devops-secrets-management-vault" 2>/dev/null | python3 -c "
import sys,re,html
t=sys.stdin.read()
t=re.sub(r'<script.*?</script>|<style.*?</style>','',t,flags=re.S)
t=re.sub(r'<[^>]+>',' ',t); t=html.unescape(t); t=re.sub(r'\s+',' ',t)
import re as r
for kw in ['price','pricing','free','trial','\$']:
for m in r.finditer(kw,t,r.I):
print(kw.upper(),'::',t[max(0,m.start()-140):m.start()+160].strip()[:300])
break
" FREE :: ea solutions through a self-guided interactive experience. Live Demo See Delinea in action with a personalized demonstration from our team. Free Trial E
FREE :: ea solutions through a self-guided interactive experience. Live Demo See Delinea in action with a personalized demonstration from our team. Free Trial Evaluate Delinea solutions in your own environment with a free trial. The High Stakes of Securing AI View the Guide Customers Customer Experience Cus TRIAL :: lutions through a self-guided interactive experience. Live Demo See Delinea in action with a personalized demonstration from our team. Free Trial Evaluate Delinea solutions in your own environment with a free trial. The High Stakes of Securing AI View the Guide Customers Customer Experience Customer $ :: Solutions Briefs Trials Videos Webinars White Papers © 2026 Copyright Delinea Privacy Policy Terms of Use MSLA Sitemap Your Privacy Choices
Recorded tool call
{
"type": "tool_call",
"seq": 32,
"ts": "2026-11-05T16:05:49.378Z",
"name": "bash",
"turn": 1,
"input": {
"command": "timeout 20 curl -sSL \"https://delinea.com/products/devops-secrets-management-vault\" 2>/dev/null | python3 -c \"\nimport sys,re,html\nt=sys.stdin.read()\nt=re.sub(r'<script.*?</script>|<style.*?</style>','',t,flags=re.S)\nt=re.sub(r'<[^>]+>',' ',t); t=html.unescape(t); t=re.sub(r'\\s+',' ',t)\nimport re as r\nfor kw in ['price','pricing','free','trial','\\$']:\n for m in r.finditer(kw,t,r.I):\n print(kw.upper(),'::',t[max(0,m.start()-140):m.start()+160].strip()[:300])\n break\n\" "
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-89644163d5024d58",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 33,
"ts": "2026-11-05T16:05:50.732Z",
"turn": 1,
"output": {
"content": [
{
"text": "FREE :: ea solutions through a self-guided interactive experience. Live Demo See Delinea in action with a personalized demonstration from our team. Free Trial Evaluate Delinea solutions in your own environment with a free trial. The High Stakes of Securing AI View the Guide Customers Customer Experience Cus\nTRIAL :: lutions through a self-guided interactive experience. Live Demo See Delinea in action with a personalized demonstration from our team. Free Trial Evaluate Delinea solutions in your own environment with a free trial. The High Stakes of Securing AI View the Guide Customers Customer Experience Customer\n$ :: Solutions Briefs Trials Videos Webinars White Papers © 2026 Copyright Delinea Privacy Policy Terms of Use MSLA Sitemap Your Privacy Choices\n",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-89644163d5024d58",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
No public pricing on product page. Let me check the DSV docs for pricing/limits, and check whether there's a public pricing page. Quick check: docs.delinea.com DSV has "pricing" section? Let me do a quick search of docs.delinea.com for pricing. Might be slow. Keep it brief — one or two curls.
Let me read the SDK source to write an accurate example.
✓read /sandbox/repo/.venv/lib/python3.12/site-packages/delinea/secrets/vault.py✓└{"content":[{"text":"\"\"\" The Delinea DevOps Secrets Vault (DSV) SDK API facilitates access to DSV\nusing bearer token authentication.\n\nExample::\n autho…ctrl+o
{"content":[{"text":"\"\"\" The Delinea DevOps Secrets Vault (DSV) SDK API facilitates access to DSV\nusing bearer token authentication.\n\nExample::\n authorizer = PasswordGrantAuthorizer(\"https://mytenant.secretsvaultcloud.com/\", \"my_client_id\", \"my_client_secret\")\n vault = SecretsVault(\"https://mytenant.secretsvaultcloud.com/\", authorizer)\n # to get the secret as a ``dict``\n secret = vault.get_secret(\"/path/to/secret\")\n # or, to use the dataclass\n secret = VaultSecret(**vault.get_secret(\"/path/to/secret\"))\"\"\"\n\nfrom abc import ABC, abstractmethod\nimport json\nimport re\nimport requests\n\nfrom dataclasses import dataclass\nfrom datetime import datetime, timedelta\n\n\n@dataclass\nclass VaultSecret:\n id: str\n path: str\n attributes: str\n description: str\n data: dict\n created: datetime\n last_modified: datetime\n created_by: str\n last_modified_by: str\n version: float\n\n DATETIME_FORMAT_PARAMETER = \"datetime_format\"\n DEFAULT_DATETIME_FORMAT = \"%Y-%m-%dT%H:%M:%SZ\"\n\n # Based on https://gist.github.com/jaytaylor/3660565\n @staticmethod\n def snake_case(camel_cased):\n \"\"\"Transform to snake case\n\n Transforms the keys of the given map from camelCase to snake_case.\n \"\"\"\n return [\n (\n re.compile(\"([a-z0-9])([A-Z])\")\n .sub(r\"\\1_\\2\", re.compile(r\"(.)([A-Z][a-z]+)\").sub(r\"\\1_\\2\", k))\n .lower(),\n v,\n )\n for (k, v) in camel_cased.items()\n ]\n\n def __init__(self, **kwargs):\n # The REST API returns attributes with camelCase names which we replace\n # with snake_case per Python conventions\n datetime_format = self.DEFAULT_DATETIME_FORMAT\n if self.DATETIME_FORMAT_PARAMETER in kwargs:\n datetime_format = kwargs[self.DATETIME_FORMAT_PARAMETER]\n for k, v in self.snake_case(kwargs):\n # @dataclass does not marshal timestamps into datetimes automatically\n if k in [\"created\", \"last_modified\"]:\n v = datetime.strptime(v, datetime_format)\n setattr(self, k, v)\n\n\nclass SecretsVaultError(Exception):\n \"\"\"An Exception that embeds the server response\"\"\"\n\n response: None\n\n def __init__(self, message, response=None, *args, **kwargs):\n self.message = message\n self.response = response\n super().__init__(*args, **kwargs)\n\n\nclass SecretsVaultAccessError(SecretsVaultError):\n \"\"\"An Exception that represents a 403\"\"\"\n\n\nclass Authorizer(ABC):\n \"\"\"Main abstract base class for all Authorizer access methods.\"\"\"\n\n @staticmethod\n def add_bearer_token_authorization_header(bearer_token, existing_headers={}):\n \"\"\"Adds an HTTP `Authorization` header containing the `Bearer` token\n :param existing_headers: a ``dict`` containing the existing headers\n :return: a ``dict`` containing the `existing_headers` and the\n `Authorization` header\n :rtype: ``dict``\n \"\"\"\n\n return {\n \"Authorization\": \"Bearer \" + bearer_token,\n **existing_headers,\n }\n\n @abstractmethod\n def get_access_token(self):\n \"\"\"Returns the access_token from a Grant Request\"\"\"\n\n def headers(self, existing_headers={}):\n \"\"\"Returns a dictionary containing headers for REST API calls\"\"\"\n return self.add_bearer_token_authorization_header(\n self.get_access_token(), existing_headers\n )\n\n\nclass AccessTokenAuthorizer(Authorizer):\n \"\"\"Allows the use of a pre-existing access token to authorize REST API\n calls.\n \"\"\"\n\n def get_access_token(self):\n return self.access_token\n\n def __init__(self, access_token):\n self.access_token = access_token\n\n\nclass PasswordGrantAuthorizer(Authorizer):\n \"\"\"Allows the use of a username and password to be used to authorize REST\n API calls.\n \"\"\"\n\n @staticmethod\n def _get_access_grant(token_url, client_id, client_secret):\n \"\"\"Gets an Access Grant by calling the DSV REST API ``token`` endpoint\n\n :raise :class:`SecretsVaultError` when the server returns anything other\n than a valid Access Grant\"\"\"\n\n response = requests.post(\n token_url,\n json={\n \"client_id\": client_id,\n \"client_secret\": client_secret,\n \"grant_type\": \"client_credentials\",\n },\n )\n\n try:\n return json.loads(SecretsVault.process(response).content)\n except json.JSONDecodeError:\n raise SecretsVaultError(response)\n\n def _refresh_access_grant(self, seconds_of_drift=300):\n \"\"\"Refreshes the Access Grant if it has expired or will in the next\n `seconds_of_drift` seconds.\n\n :raise :class:`SecretsVaultError` when the server returns anything other\n than a valid Access Grant\"\"\"\n\n if (\n hasattr(self, \"access_grant\")\n and self.access_grant_refreshed\n + timedelta(seconds=self.access_grant[\"expiresIn\"] + seconds_of_drift)\n > datetime.now()\n ):\n return\n else:\n self.access_grant = self._get_access_grant(\n self.token_url, self.client_id, self.client_secret\n )\n self.access_grant_refreshed = datetime.now()\n\n def __init__(self, base_url, client_id, client_secret, token_path_uri=\"/v1/token\"):\n self.base_url = base_url\n self.client_id = client_id\n self.client_secret = client_secret\n self.token_url = base_url.rstrip(\"/\") + \"/\" + token_path_uri.lstrip(\"/\")\n self.grant_request = {\n \"client_id\": self.client_id,\n \"client_secret\": self.client_secret,\n \"grant_type\": \"client_credentials\",\n }\n\n def get_access_token(self):\n self._refresh_access_grant()\n return self.access_grant[\"accessToken\"]\n\n\nclass SecretsVault:\n \"\"\"A class that uses bearer token authentication to access the DSV API.\n\n It Uses :attr:`base_url`, :attr:`authorizer` to make calls to the DSV REST\n API\n \"\"\"\n\n SECRET_PATH_URI = \"secrets\"\n\n @staticmethod\n def process(response):\n \"\"\"Process the response raising an error if the call was unsuccessful\n\n :param response: the response from the server\n :type response: :class:`~requests.Response`\n :return: the response if the call was successful\n :rtype: :class:`~requests.Response`\n :raises: :class:`SecretsVaultAccessError` when the caller does not have\n access to the secret\n :raises: :class:`SecretsAccessError` when the server responses with any\n other error\"\"\"\n\n if response.status_code >= 200 and response.status_code < 300:\n return response\n if response.status_code >= 400 and response.status_code < 500:\n raise SecretsVaultAccessError(\n json.loads(response.content)[\"message\"], response\n )\n raise SecretsVaultError(response)\n\n def __init__(\n self,\n base_url,\n authorizer: Authorizer,\n api_path_uri=\"/v1\",\n ):\n \"\"\"\n :param base_url: The DSV URL i.e. mytenant.secretsvaultcloud.com\n :type base_url: str\n :param authorizer: The Authorizer class used to authenticate to the DSV\n REST API\n :type authorizer: Authorizer class\n \"\"\"\n\n self.base_url = base_url.rstrip(\"/\")\n self.authorizer = authorizer\n self.api_url = base_url.rstrip(\"/\") + \"/\" + api_path_uri.strip(\"/\")\n\n def headers(self):\n \"\"\"Returns a dictionary containing HTTP headers.\"\"\"\n return self.authorizer.headers()\n\n def get_secret_json(self, secret_path):\n \"\"\"Gets a secret from DSV\n\n :param secret_path: the path to the secret\n :type secret_path: str\n :return: a JSON formatted string representation of the secret\n :rtype: ``str``\n :raise: :class:`SecretsVaultAccessError` when the caller does not have\n permission to access the secret\n :raise: :class:`SecretsVaultError` when the REST API call fails for\n any other reason\"\"\"\n\n return self.process(\n requests.get(\n f\"{self.api_url}/{self.SECRET_PATH_URI}/{secret_path.lstrip('/')}\",\n headers=self.headers(),\n )\n ).text\n\n def get_secret(self, secret_path):\n \"\"\"Gets a secret\n\n :param secret_path: the path to the secret\n :type secret_path: str\n :return: a ``dict`` representation of the secret\n :rtype: ``dict``\n :raise: :class:`SecretsVaultAccessError` when the caller does not have\n permission to access the secret\n :raise: :class:`SecretsVaultError` when the REST API call fails for\n any other reason\"\"\"\n response = self.get_secret_json(secret_path)\n\n try:\n return json.loads(response)\n except json.JSONDecodeError:\n raise SecretsVaultError(response)\n","type":"text"}]}timeout 25 curl -sSL "https://docs.delinea.com/dsv/current/pricing.htm" -o /tmp/p1.html -w '%{http_code}\n'; timeout 25 curl -sSL "https://delinea.com/pricing" -o /tmp/p2.html -w '%{http_code}\n'; for f in /tmp/p1.html /tmp/p2.html; do echo "== $f"; python3 -c "
import sys,re,html
t=open('$f',errors='ignore').read()
t=re.sub(r'<script.*?</script>|<style.*?</style>','',t,flags=re.S)
t=re.sub(r'<[^>]+>',' ',t); t=html.unescape(t); t=re.sub(r'\s+',' ',t)
print(t[:1200])
"; done404
404 404 == /tmp/p1.html == /tmp/p2.html Skip to content Blog Contact Start Demo Solutions Challenges Industries we serve Capabilities Identities Services we provide Accelerate AI Securely Enable secure AI adoption with visibility, governance, and control across identities, agents, and access Defend Against AI Attacks Protect your organization from AI-driven threats by securing identities, controlling access, and reducing attack paths. Cyber Insurance Improve your cyber insurance readiness with stronger identity security, governance, and privileged access controls. Ransomware Defend against ransomware by securing privileged identities, limiting access, and reducing attack paths. Vendor Consolidation Reduce complexity, strengthen security and improve IT ROI through platform consolidation. Secure Remote Workforce Secure access for employees, contractors, and third parties from anywhere. Reduce Identity Risk Gain visibility, control, and continuous authorization across every identity. Financial Services Reduce identity risk and strengthen governance across financial institutions. Healthcare Secure access to critical systems, patient data, and clinical environments. Federal & Public Support secure access, compliance, and ope
Recorded tool call
{
"type": "tool_call",
"seq": 38,
"ts": "2026-11-05T16:06:03.318Z",
"name": "bash",
"turn": 1,
"input": {
"command": "timeout 25 curl -sSL \"https://docs.delinea.com/dsv/current/pricing.htm\" -o /tmp/p1.html -w '%{http_code}\\n'; timeout 25 curl -sSL \"https://delinea.com/pricing\" -o /tmp/p2.html -w '%{http_code}\\n'; for f in /tmp/p1.html /tmp/p2.html; do echo \"== $f\"; python3 -c \"\nimport sys,re,html\nt=open('$f',errors='ignore').read()\nt=re.sub(r'<script.*?</script>|<style.*?</style>','',t,flags=re.S)\nt=re.sub(r'<[^>]+>',' ',t); t=html.unescape(t); t=re.sub(r'\\s+',' ',t)\nprint(t[:1200])\n\"; done"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-b4d6c89061842cfc",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 40,
"ts": "2026-11-05T16:06:04.811Z",
"turn": 1,
"output": {
"content": [
{
"text": "404\n404\n== /tmp/p1.html\n\n== /tmp/p2.html\n Skip to content Blog Contact Start Demo Solutions Challenges Industries we serve Capabilities Identities Services we provide Accelerate AI Securely Enable secure AI adoption with visibility, governance, and control across identities, agents, and access Defend Against AI Attacks Protect your organization from AI-driven threats by securing identities, controlling access, and reducing attack paths. Cyber Insurance Improve your cyber insurance readiness with stronger identity security, governance, and privileged access controls. Ransomware Defend against ransomware by securing privileged identities, limiting access, and reducing attack paths. Vendor Consolidation Reduce complexity, strengthen security and improve IT ROI through platform consolidation. Secure Remote Workforce Secure access for employees, contractors, and third parties from anywhere. Reduce Identity Risk Gain visibility, control, and continuous authorization across every identity. Financial Services Reduce identity risk and strengthen governance across financial institutions. Healthcare Secure access to critical systems, patient data, and clinical environments. Federal & Public Support secure access, compliance, and ope\n",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-b4d6c89061842cfc",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
Pricing isn't public — quote-based. I'll state that plainly with what I could verify (free trial, no published list prices) and general known model (subscription, per-user/per-secret tiers) but hedge appropriately. I should be careful not to fabricate numbers.
Now build the example. Files:
- README.md
- requirements.txt
- .env.example
- dsv_example.py — reads DSV_BASE_URL, DSV_CLIENT_ID/SECRET or DSV_ACCESS_TOKEN, secret path from argv/env; prints non-secret metadata plus optionally data keys; handles errors.
- tests/test_dsv_example.py — spins an ephemeral in-process mock DSV server (threaded http.server, no external stack) and asserts get_secret works via SDK against it. Keep quick.
Also maybe a small "config loader" showing typical use: load DB creds from vault into app config. Keep simple: one module app.py? Let's do:
dsv_example.py: main demo with functions: build_vault(), fetch_db_config(path), main()tests/test_dsv_example.py: mock server test
Run pytest to verify.
Let me write it.
File edits
1 file
Writtenrequirements.txtView changes
/sandbox/repo/requirements.txt
Written content · previous contents were not recorded
python-dsv-sdk==1.0.4pytest>=7.0
Source: write · chatcmpl-tool-8683392ee9f2e97f
Recorded tool call
{
"type": "tool_call",
"seq": 43,
"ts": "2026-11-05T16:06:07.647Z",
"name": "write",
"turn": 1,
"input": {
"path": "/sandbox/repo/requirements.txt",
"content": "python-dsv-sdk==1.0.4\npytest>=7.0\n"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-8683392ee9f2e97f",
"tool_kind": "file_edit"
}Recorded tool result
{
"type": "tool_result",
"seq": 46,
"ts": "2026-11-05T16:06:07.647Z",
"turn": 1,
"output": {
"content": [
{
"text": "Successfully wrote 34 bytes to /sandbox/repo/requirements.txt",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-8683392ee9f2e97f",
"is_error": false,
"tool_kind": "file_edit"
}File edits
1 file
Written.env.exampleView changes
/sandbox/repo/.env.example
Written content · previous contents were not recorded
# Delinea DevOps Secrets Vault (DSV) - hosted SaaS. No local services needed.# Tenant URL, e.g. https://mytenant.secretsvaultcloud.comDSV_BASE_URL=# Option A: client credentials (created in DSV: Admin -> Clients)DSV_CLIENT_ID=DSV_CLIENT_SECRET=# Option B: pre-existing bearer token (takes precedence if set)DSV_ACCESS_TOKEN=# Secret path the client role is allowed to readDSV_SECRET_PATH=/examples/app/db
Source: write · chatcmpl-tool-ac8b2a2673642341
Recorded tool call
{
"type": "tool_call",
"seq": 44,
"ts": "2026-11-05T16:06:07.647Z",
"name": "write",
"turn": 1,
"input": {
"path": "/sandbox/repo/.env.example",
"content": "# Delinea DevOps Secrets Vault (DSV) - hosted SaaS. No local services needed.\n# Tenant URL, e.g. https://mytenant.secretsvaultcloud.com\nDSV_BASE_URL=\n# Option A: client credentials (created in DSV: Admin -> Clients)\nDSV_CLIENT_ID=\nDSV_CLIENT_SECRET=\n# Option B: pre-existing bearer token (takes precedence if set)\nDSV_ACCESS_TOKEN=\n# Secret path the client role is allowed to read\nDSV_SECRET_PATH=/examples/app/db\n"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-ac8b2a2673642341",
"tool_kind": "file_edit"
}Recorded tool result
{
"type": "tool_result",
"seq": 45,
"ts": "2026-11-05T16:06:07.647Z",
"turn": 1,
"output": {
"content": [
{
"text": "Successfully wrote 414 bytes to /sandbox/repo/.env.example",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-ac8b2a2673642341",
"is_error": false,
"tool_kind": "file_edit"
}›Thinking…
Now the example module.
Note: the SDK's get_secret only supports reads. For write/admin ops you need the raw REST API (v1/secrets POST with role-based auth). Keep the example read-only plus show a raw REST helper for creating a secret? That would require admin token. I'll include an optional create_secret via REST with a bearer token, guarded. Keep light: I'll include a small rest.py? No — keep one module with functions: build_vault, fetch_secret, to_env_dict, main. Maybe include an optional create_secret using requests for completeness, marked optional.
Keep it to one file, clean.
File edits
1 file
Writtendsv_example.pyView changes
/sandbox/repo/dsv_example.py
Written content · previous contents were not recorded
"""Minimal working example for Delinea DevOps Secrets Vault (DSV).DSV is Delinea's hosted (SaaS) secrets manager, so this example talks to aremote tenant over HTTPS with the official Python SDK (`python-dsv-sdk`).Nothing has to run locally: no containers, no server processes.Typical flow shown here:1. Build an authorizer (client credentials, or a pre-existing bearer token).2. Build a `SecretsVault` client pointed at your tenant URL.3. Read a secret by path and turn it into plain env-style config.Run it:export DSV_BASE_URL="https://mytenant.secretsvaultcloud.com"export DSV_CLIENT_ID="..." DSV_CLIENT_SECRET="..."python dsv_example.py /examples/app/db"""from __future__ import annotationsimport argparseimport jsonimport osimport sysfrom delinea.secrets.vault import (AccessTokenAuthorizer,Authorizer,PasswordGrantAuthorizer,SecretsVault,SecretsVaultAccessError,SecretsVaultError,VaultSecret,)DEFAULT_SECRET_PATH = "/examples/app/db"def build_authorizer(base_url: str,client_id: str | None = None,client_secret: str | None = None,access_token: str | None = None,) -> Authorizer:"""Return an authorizer for DSV.A pre-existing bearer token wins; otherwise client credentials are used andthe SDK handles token acquisition/refresh for you."""if access_token:return AccessTokenAuthorizer(access_token)if not (client_id and client_secret):raise ValueError("Set DSV_ACCESS_TOKEN, or both DSV_CLIENT_ID and DSV_CLIENT_SECRET.")return PasswordGrantAuthorizer(base_url, client_id, client_secret)def build_vault(base_url: str | None = None,client_id: str | None = None,client_secret: str | None = None,access_token: str | None = None,) -> SecretsVault:"""Build a DSV client from explicit args or DSV_* environment variables."""base_url = base_url or os.environ.get("DSV_BASE_URL")if not base_url:raise ValueError("Set DSV_BASE_URL to your tenant URL.")authorizer = build_authorizer(base_url,client_id or os.environ.get("DSV_CLIENT_ID"),client_secret or os.environ.get("DSV_CLIENT_SECRET"),access_token or os.environ.get("DSV_ACCESS_TOKEN"),)return SecretsVault(base_url, authorizer)def fetch_secret(vault: SecretsVault, path: str) -> VaultSecret:"""Read one secret and return it as a typed VaultSecret dataclass."""return VaultSecret(**vault.get_secret(path))def to_env_dict(secret: VaultSecret, prefix: str = "") -> dict[str, str]:"""Flatten secret data into env-style key/value strings.This is the usual "vault -> app config" step: the values stay in memory andare never written to disk or logged."""return {f"{prefix}{key}".upper(): str(value) for key, value in secret.data.items()}def main(argv: list[str] | None = None) -> int:parser = argparse.ArgumentParser(description="Fetch a secret from Delinea DSV.")parser.add_argument("path", nargs="?", default=os.environ.get("DSV_SECRET_PATH", DEFAULT_SECRET_PATH))parser.add_argument("--show-values", action="store_true", help="Print secret values (not just keys).")args = parser.parse_args(argv)try:vault = build_vault()secret = fetch_secret(vault, args.path)except SecretsVaultAccessError as exc:# 4xx: bad credentials, or the client role cannot read this path.print(f"access denied for {args.path!r}: {exc.message}", file=sys.stderr)return 2except SecretsVaultError as exc:print(f"DSV call failed: {exc}", file=sys.stderr)return 1except ValueError as exc:print(f"configuration error: {exc}", file=sys.stderr)return 3config = to_env_dict(secret, prefix="DB_")print(f"path: {secret.path}")print(f"id: {secret.id}")print(f"version: {secret.version}")print(f"modified: {secret.last_modified.isoformat()}Z by {secret.last_modified_by}")if args.show_values:print(json.dumps(config, indent=2))else:print(f"keys loaded: {sorted(config)} (use --show-values to print values)")return 0if __name__ == "__main__":raise SystemExit(main())
Source: write · chatcmpl-tool-a6ccfd74dae76642
Recorded tool call
{
"type": "tool_call",
"seq": 49,
"ts": "2026-11-05T16:06:14.740Z",
"name": "write",
"turn": 1,
"input": {
"path": "/sandbox/repo/dsv_example.py",
"content": "\"\"\"Minimal working example for Delinea DevOps Secrets Vault (DSV).\n\nDSV is Delinea's hosted (SaaS) secrets manager, so this example talks to a\nremote tenant over HTTPS with the official Python SDK (`python-dsv-sdk`).\nNothing has to run locally: no containers, no server processes.\n\nTypical flow shown here:\n 1. Build an authorizer (client credentials, or a pre-existing bearer token).\n 2. Build a `SecretsVault` client pointed at your tenant URL.\n 3. Read a secret by path and turn it into plain env-style config.\n\nRun it:\n export DSV_BASE_URL=\"https://mytenant.secretsvaultcloud.com\"\n export DSV_CLIENT_ID=\"...\" DSV_CLIENT_SECRET=\"...\"\n python dsv_example.py /examples/app/db\n\"\"\"\n\nfrom __future__ import annotations\n\nimport argparse\nimport json\nimport os\nimport sys\n\nfrom delinea.secrets.vault import (\n AccessTokenAuthorizer,\n Authorizer,\n PasswordGrantAuthorizer,\n SecretsVault,\n SecretsVaultAccessError,\n SecretsVaultError,\n VaultSecret,\n)\n\nDEFAULT_SECRET_PATH = \"/examples/app/db\"\n\n\ndef build_authorizer(\n base_url: str,\n client_id: str | None = None,\n client_secret: str | None = None,\n access_token: str | None = None,\n) -> Authorizer:\n \"\"\"Return an authorizer for DSV.\n\n A pre-existing bearer token wins; otherwise client credentials are used and\n the SDK handles token acquisition/refresh for you.\n \"\"\"\n if access_token:\n return AccessTokenAuthorizer(access_token)\n if not (client_id and client_secret):\n raise ValueError(\n \"Set DSV_ACCESS_TOKEN, or both DSV_CLIENT_ID and DSV_CLIENT_SECRET.\"\n )\n return PasswordGrantAuthorizer(base_url, client_id, client_secret)\n\n\ndef build_vault(\n base_url: str | None = None,\n client_id: str | None = None,\n client_secret: str | None = None,\n access_token: str | None = None,\n) -> SecretsVault:\n \"\"\"Build a DSV client from explicit args or DSV_* environment variables.\"\"\"\n base_url = base_url or os.environ.get(\"DSV_BASE_URL\")\n if not base_url:\n raise ValueError(\"Set DSV_BASE_URL to your tenant URL.\")\n authorizer = build_authorizer(\n base_url,\n client_id or os.environ.get(\"DSV_CLIENT_ID\"),\n client_secret or os.environ.get(\"DSV_CLIENT_SECRET\"),\n access_token or os.environ.get(\"DSV_ACCESS_TOKEN\"),\n )\n return SecretsVault(base_url, authorizer)\n\n\ndef fetch_secret(vault: SecretsVault, path: str) -> VaultSecret:\n \"\"\"Read one secret and return it as a typed VaultSecret dataclass.\"\"\"\n return VaultSecret(**vault.get_secret(path))\n\n\ndef to_env_dict(secret: VaultSecret, prefix: str = \"\") -> dict[str, str]:\n \"\"\"Flatten secret data into env-style key/value strings.\n\n This is the usual \"vault -> app config\" step: the values stay in memory and\n are never written to disk or logged.\n \"\"\"\n return {\n f\"{prefix}{key}\".upper(): str(value) for key, value in secret.data.items()\n }\n\n\ndef main(argv: list[str] | None = None) -> int:\n parser = argparse.ArgumentParser(description=\"Fetch a secret from Delinea DSV.\")\n parser.add_argument(\"path\", nargs=\"?\", default=os.environ.get(\"DSV_SECRET_PATH\", DEFAULT_SECRET_PATH))\n parser.add_argument(\"--show-values\", action=\"store_true\", help=\"Print secret values (not just keys).\")\n args = parser.parse_args(argv)\n\n try:\n vault = build_vault()\n secret = fetch_secret(vault, args.path)\n except SecretsVaultAccessError as exc:\n # 4xx: bad credentials, or the client role cannot read this path.\n print(f\"access denied for {args.path!r}: {exc.message}\", file=sys.stderr)\n return 2\n except SecretsVaultError as exc:\n print(f\"DSV call failed: {exc}\", file=sys.stderr)\n return 1\n except ValueError as exc:\n print(f\"configuration error: {exc}\", file=sys.stderr)\n return 3\n\n config = to_env_dict(secret, prefix=\"DB_\")\n print(f\"path: {secret.path}\")\n print(f\"id: {secret.id}\")\n print(f\"version: {secret.version}\")\n print(f\"modified: {secret.last_modified.isoformat()}Z by {secret.last_modified_by}\")\n if args.show_values:\n print(json.dumps(config, indent=2))\n else:\n print(f\"keys loaded: {sorted(config)} (use --show-values to print values)\")\n return 0\n\n\nif __name__ == \"__main__\":\n raise SystemExit(main())\n"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-a6ccfd74dae76642",
"tool_kind": "file_edit"
}Recorded tool result
{
"type": "tool_result",
"seq": 50,
"ts": "2026-11-05T16:06:14.740Z",
"turn": 1,
"output": {
"content": [
{
"text": "Successfully wrote 4323 bytes to /sandbox/repo/dsv_example.py",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-a6ccfd74dae76642",
"is_error": false,
"tool_kind": "file_edit"
}›Thinking…
Now a test with an in-process ephemeral mock (thread + http.server, dies with the test). That's not a "service stack".
File edits
1 file
Writtentest_dsv_example.pyView changes
/sandbox/repo/test_dsv_example.py
Written content · previous contents were not recorded
"""Offline test: exercises dsv_example against an in-process fake DSV endpoint.The fake server lives inside the test process and shuts down when the test ends,so nothing has to be installed or left running. It proves the wiring (tokenrequest -> bearer-authenticated secret read -> config mapping) is correct beforeyou point the same code at a real tenant."""from __future__ import annotationsimport jsonimport threadingfrom http.server import BaseHTTPRequestHandler, ThreadingHTTPServerimport pytestimport dsv_examplefrom delinea.secrets.vault import SecretsVaultAccessErrorSECRET_PAYLOAD = {"id": "abc123","path": "/examples/app/db","attributes": "","description": "example db credential","data": {"username": "app", "password": "s3cret"},"created": "2024-01-01T00:00:00Z","lastModified": "2024-02-01T00:00:00Z","createdBy": "admin","lastModifiedBy": "admin","version": 1.0,}class FakeDsv(BaseHTTPRequestHandler):tokens_issued = 0def log_message(self, *args): # silence request loggingpassdef _send(self, code, body):raw = json.dumps(body).encode()self.send_response(code)self.send_header("Content-Type", "application/json")self.send_header("Content-Length", str(len(raw)))self.end_headers()self.wfile.write(raw)def do_POST(self):if self.path == "/v1/token":FakeDsv.tokens_issued += 1self._send(200, {"accessToken": "fake-token", "expiresIn": 3600})else:self._send(404, {"message": "not found"})def do_GET(self):if self.headers.get("Authorization") != "Bearer fake-token":self._send(403, {"message": "invalid token"})elif self.path == "/v1/secrets/examples/app/db":self._send(200, SECRET_PAYLOAD)else:self._send(403, {"message": "access denied"})@pytest.fixture()def tenant():server = ThreadingHTTPServer(("127.0.0.1", 0), FakeDsv)thread = threading.Thread(target=server.serve_forever, daemon=True)thread.start()try:yield f"http://127.0.0.1:{server.server_port}"finally:server.shutdown()server.server_close()def test_fetch_and_flatten(tenant):vault = dsv_example.build_vault(tenant, client_id="cid", client_secret="csecret")secret = dsv_example.fetch_secret(vault, "/examples/app/db")assert secret.path == "/examples/app/db"assert secret.data["username"] == "app"assert dsv_example.to_env_dict(secret, prefix="DB_") == {"DB_USERNAME": "app","DB_PASSWORD": "s3cret",}assert FakeDsv.tokens_issued == 1 # token is fetched once and reuseddef test_token_is_reused_across_reads(tenant):vault = dsv_example.build_vault(tenant, client_id="cid", client_secret="csecret")dsv_example.fetch_secret(vault, "/examples/app/db")dsv_example.fetch_secret(vault, "/examples/app/db")assert FakeDsv.tokens_issued == 1def test_access_denied_raises(tenant):vault = dsv_example.build_vault(tenant, client_id="cid", client_secret="csecret")with pytest.raises(SecretsVaultAccessError):dsv_example.fetch_secret(vault, "/not/allowed")def test_missing_credentials_is_a_config_error():with pytest.raises(ValueError):dsv_example.build_vault("https://example.secretsvaultcloud.com")def test_cli_exit_code_on_access_denied(tenant, monkeypatch, capsys):monkeypatch.setenv("DSV_BASE_URL", tenant)monkeypatch.setenv("DSV_CLIENT_ID", "cid")monkeypatch.setenv("DSV_CLIENT_SECRET", "csecret")assert dsv_example.main(["/not/allowed"]) == 2assert "access denied" in capsys.readouterr().errdef test_cli_happy_path(tenant, monkeypatch, capsys):monkeypatch.setenv("DSV_BASE_URL", tenant)monkeypatch.setenv("DSV_CLIENT_ID", "cid")monkeypatch.setenv("DSV_CLIENT_SECRET", "csecret")assert dsv_example.main(["/examples/app/db"]) == 0out = capsys.readouterr().outassert "DB_PASSWORD" in outassert "s3cret" not in out # values hidden unless --show-values
Source: write · chatcmpl-tool-b6a27129e2cc848a
Recorded tool call
{
"type": "tool_call",
"seq": 53,
"ts": "2026-11-05T16:06:21.344Z",
"name": "write",
"turn": 1,
"input": {
"path": "/sandbox/repo/test_dsv_example.py",
"content": "\"\"\"Offline test: exercises dsv_example against an in-process fake DSV endpoint.\n\nThe fake server lives inside the test process and shuts down when the test ends,\nso nothing has to be installed or left running. It proves the wiring (token\nrequest -> bearer-authenticated secret read -> config mapping) is correct before\nyou point the same code at a real tenant.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport json\nimport threading\nfrom http.server import BaseHTTPRequestHandler, ThreadingHTTPServer\n\nimport pytest\n\nimport dsv_example\nfrom delinea.secrets.vault import SecretsVaultAccessError\n\nSECRET_PAYLOAD = {\n \"id\": \"abc123\",\n \"path\": \"/examples/app/db\",\n \"attributes\": \"\",\n \"description\": \"example db credential\",\n \"data\": {\"username\": \"app\", \"password\": \"s3cret\"},\n \"created\": \"2024-01-01T00:00:00Z\",\n \"lastModified\": \"2024-02-01T00:00:00Z\",\n \"createdBy\": \"admin\",\n \"lastModifiedBy\": \"admin\",\n \"version\": 1.0,\n}\n\n\nclass FakeDsv(BaseHTTPRequestHandler):\n tokens_issued = 0\n\n def log_message(self, *args): # silence request logging\n pass\n\n def _send(self, code, body):\n raw = json.dumps(body).encode()\n self.send_response(code)\n self.send_header(\"Content-Type\", \"application/json\")\n self.send_header(\"Content-Length\", str(len(raw)))\n self.end_headers()\n self.wfile.write(raw)\n\n def do_POST(self):\n if self.path == \"/v1/token\":\n FakeDsv.tokens_issued += 1\n self._send(200, {\"accessToken\": \"fake-token\", \"expiresIn\": 3600})\n else:\n self._send(404, {\"message\": \"not found\"})\n\n def do_GET(self):\n if self.headers.get(\"Authorization\") != \"Bearer fake-token\":\n self._send(403, {\"message\": \"invalid token\"})\n elif self.path == \"/v1/secrets/examples/app/db\":\n self._send(200, SECRET_PAYLOAD)\n else:\n self._send(403, {\"message\": \"access denied\"})\n\n\n@pytest.fixture()\ndef tenant():\n server = ThreadingHTTPServer((\"127.0.0.1\", 0), FakeDsv)\n thread = threading.Thread(target=server.serve_forever, daemon=True)\n thread.start()\n try:\n yield f\"http://127.0.0.1:{server.server_port}\"\n finally:\n server.shutdown()\n server.server_close()\n\n\ndef test_fetch_and_flatten(tenant):\n vault = dsv_example.build_vault(tenant, client_id=\"cid\", client_secret=\"csecret\")\n secret = dsv_example.fetch_secret(vault, \"/examples/app/db\")\n\n assert secret.path == \"/examples/app/db\"\n assert secret.data[\"username\"] == \"app\"\n assert dsv_example.to_env_dict(secret, prefix=\"DB_\") == {\n \"DB_USERNAME\": \"app\",\n \"DB_PASSWORD\": \"s3cret\",\n }\n assert FakeDsv.tokens_issued == 1 # token is fetched once and reused\n\n\ndef test_token_is_reused_across_reads(tenant):\n vault = dsv_example.build_vault(tenant, client_id=\"cid\", client_secret=\"csecret\")\n dsv_example.fetch_secret(vault, \"/examples/app/db\")\n dsv_example.fetch_secret(vault, \"/examples/app/db\")\n assert FakeDsv.tokens_issued == 1\n\n\ndef test_access_denied_raises(tenant):\n vault = dsv_example.build_vault(tenant, client_id=\"cid\", client_secret=\"csecret\")\n with pytest.raises(SecretsVaultAccessError):\n dsv_example.fetch_secret(vault, \"/not/allowed\")\n\n\ndef test_missing_credentials_is_a_config_error():\n with pytest.raises(ValueError):\n dsv_example.build_vault(\"https://example.secretsvaultcloud.com\")\n\n\ndef test_cli_exit_code_on_access_denied(tenant, monkeypatch, capsys):\n monkeypatch.setenv(\"DSV_BASE_URL\", tenant)\n monkeypatch.setenv(\"DSV_CLIENT_ID\", \"cid\")\n monkeypatch.setenv(\"DSV_CLIENT_SECRET\", \"csecret\")\n assert dsv_example.main([\"/not/allowed\"]) == 2\n assert \"access denied\" in capsys.readouterr().err\n\n\ndef test_cli_happy_path(tenant, monkeypatch, capsys):\n monkeypatch.setenv(\"DSV_BASE_URL\", tenant)\n monkeypatch.setenv(\"DSV_CLIENT_ID\", \"cid\")\n monkeypatch.setenv(\"DSV_CLIENT_SECRET\", \"csecret\")\n assert dsv_example.main([\"/examples/app/db\"]) == 0\n out = capsys.readouterr().out\n assert \"DB_PASSWORD\" in out\n assert \"s3cret\" not in out # values hidden unless --show-values\n"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-b6a27129e2cc848a",
"tool_kind": "file_edit"
}Recorded tool result
{
"type": "tool_result",
"seq": 54,
"ts": "2026-11-05T16:06:21.344Z",
"turn": 1,
"output": {
"content": [
{
"text": "Successfully wrote 4138 bytes to /sandbox/repo/test_dsv_example.py",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-b6a27129e2cc848a",
"is_error": false,
"tool_kind": "file_edit"
}›Thinking…
Note: pytest by default collects test_*.py; test file imports dsv_example — fine.
Run tests.
cd /sandbox/repo && . .venv/bin/activate && python -m pytest -q 2>&1 | tail -20
.F.... [100%]
.F.... [100%]
=================================== FAILURES ===================================
______________________ test_token_is_reused_across_reads _______________________
tenant = 'http://127.0.0.1:39717'
def test_token_is_reused_across_reads(tenant):
vault = dsv_example.build_vault(tenant, client_id="cid", client_secret="csecret")
dsv_example.fetch_secret(vault, "/examples/app/db")
dsv_example.fetch_secret(vault, "/examples/app/db")
> assert FakeDsv.tokens_issued == 1
E assert 2 == 1
E + where 2 = FakeDsv.tokens_issued
test_dsv_example.py:93: AssertionError
=========================== short test summary info ============================
FAILED test_dsv_example.py::test_token_is_reused_across_reads - assert 2 == 1
1 failed, 5 passed in 2.64s
Recorded tool call
{
"type": "tool_call",
"seq": 57,
"ts": "2026-11-05T16:06:24.480Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /sandbox/repo && . .venv/bin/activate && python -m pytest -q 2>&1 | tail -20"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-92d766f66826b1af",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 58,
"ts": "2026-11-05T16:06:27.215Z",
"turn": 1,
"output": {
"content": [
{
"text": ".F.... [100%]\n=================================== FAILURES ===================================\n______________________ test_token_is_reused_across_reads _______________________\n\ntenant = 'http://127.0.0.1:39717'\n\n def test_token_is_reused_across_reads(tenant):\n vault = dsv_example.build_vault(tenant, client_id=\"cid\", client_secret=\"csecret\")\n dsv_example.fetch_secret(vault, \"/examples/app/db\")\n dsv_example.fetch_secret(vault, \"/examples/app/db\")\n> assert FakeDsv.tokens_issued == 1\nE assert 2 == 1\nE + where 2 = FakeDsv.tokens_issued\n\ntest_dsv_example.py:93: AssertionError\n=========================== short test summary info ============================\nFAILED test_dsv_example.py::test_token_is_reused_across_reads - assert 2 == 1\n1 failed, 5 passed in 2.64s\n",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-92d766f66826b1af",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
Class-level counter shared across tests. Need per-test reset or instance-scoped counting. Fix: reset counter in fixture.
Also note the SDK refresh logic is arguably buggy: access_grant_refreshed + timedelta(seconds=expiresIn + drift) > now — with expiresIn=3600 and drift 300, refreshed+3900 > now → returns cached. So it should reuse. But test 1 passed with 1 issue... and test 2 got 2 — because counter carried over from test 1. So just reset.
File edits
1 file
Modifiedtest_dsv_example.py+1−0View changes
/sandbox/repo/test_dsv_example.py
Replacement excerpt
def tenant():+ FakeDsv.tokens_issued = 0server = ThreadingHTTPServer
Source: edit · chatcmpl-tool-9612feb5ee503a26
Recorded tool call
{
"type": "tool_call",
"seq": 61,
"ts": "2026-11-05T16:06:29.114Z",
"name": "edit",
"turn": 1,
"input": {
"path": "/sandbox/repo/test_dsv_example.py",
"edits": [
{
"newText": "def tenant():\n FakeDsv.tokens_issued = 0\n server = ThreadingHTTPServer",
"oldText": "def tenant():\n server = ThreadingHTTPServer"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-9612feb5ee503a26",
"tool_kind": "file_edit"
}Recorded tool result
{
"type": "tool_result",
"seq": 62,
"ts": "2026-11-05T16:06:29.114Z",
"turn": 1,
"output": {
"content": [
{
"text": "Successfully replaced 1 block(s) in /sandbox/repo/test_dsv_example.py.",
"type": "text"
}
],
"details": {
"diff": " ...\n 62 \n 63 \n 64 @pytest.fixture()\n 65 def tenant():\n+ 66 FakeDsv.tokens_issued = 0\n 66 server = ThreadingHTTPServer((\"127.0.0.1\", 0), FakeDsv)\n 67 thread = threading.Thread(target=server.serve_forever, daemon=True)\n 68 thread.start()\n 69 try:\n ...",
"patch": "--- /sandbox/repo/test_dsv_example.py\n+++ /sandbox/repo/test_dsv_example.py\n@@ -62,8 +62,9 @@\n \n \n @pytest.fixture()\n def tenant():\n+ FakeDsv.tokens_issued = 0\n server = ThreadingHTTPServer((\"127.0.0.1\", 0), FakeDsv)\n thread = threading.Thread(target=server.serve_forever, daemon=True)\n thread.start()\n try:\n",
"firstChangedLine": 66
}
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-9612feb5ee503a26",
"is_error": false,
"tool_kind": "file_edit"
}›Thinking…
Also the first test asserts tokens_issued == 1 which now is fine.
Also: is token reuse real? expiresIn 3600 → cached. Good.
Rerun.
Also: check live call behavior without creds — run the CLI to show the config error path (fast, no network). Then write README.
Additionally, should I verify the real endpoint's reachability? Could do a harmless check: POST to a bogus tenant token endpoint to confirm DNS/network — e.g. https://nonexistent-tenant-xyz.secretsvaultcloud.com/v1/token. That's a quick call showing that only credentials block me. Let's do it with timeout 10.
cd /sandbox/repo && . .venv/bin/activate && python -m pytest -q 2>&1 | tail -5; echo "== CLI without creds =="; env -u DSV_BASE_URL python dsv_example.py; echo "exit=$?"; echo "== real endpoint probe (bogus tenant) =="; timeout 12 curl -sS -o /tmp/probe.json -w 'http=%{http_code}\n' -X POST https://pi-sandbox-probe.secretsvaultcloud.com/v1/token -H 'content-type: application/json' -d '{"client_id":"x","client_secret":"y","grant_type":"client_credentials"}'; head -c 200 /tmp/probe.json...... [100%]
...... [100%]
6 passed in 2.63s
== CLI without creds ==
configuration error: Set DSV_BASE_URL to your tenant URL.
exit=3
== real endpoint probe (bogus tenant) ==
http=400
{"message":"Tenant not found"}Recorded tool call
{
"type": "tool_call",
"seq": 65,
"ts": "2026-11-05T16:06:31.970Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cd /sandbox/repo && . .venv/bin/activate && python -m pytest -q 2>&1 | tail -5; echo \"== CLI without creds ==\"; env -u DSV_BASE_URL python dsv_example.py; echo \"exit=$?\"; echo \"== real endpoint probe (bogus tenant) ==\"; timeout 12 curl -sS -o /tmp/probe.json -w 'http=%{http_code}\\n' -X POST https://pi-sandbox-probe.secretsvaultcloud.com/v1/token -H 'content-type: application/json' -d '{\"client_id\":\"x\",\"client_secret\":\"y\",\"grant_type\":\"client_credentials\"}'; head -c 200 /tmp/probe.json"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-90a1bf76dd514547",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 66,
"ts": "2026-11-05T16:06:35.352Z",
"turn": 1,
"output": {
"content": [
{
"text": "...... [100%]\n6 passed in 2.63s\n== CLI without creds ==\nconfiguration error: Set DSV_BASE_URL to your tenant URL.\nexit=3\n== real endpoint probe (bogus tenant) ==\nhttp=400\n{\"message\":\"Tenant not found\"}",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-90a1bf76dd514547",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
The hosted API is reachable; only credentials block live calls. Good — I can state that plainly.
Now README with run instructions, pricing section, and management assessment. Then commit? Repo has .git; I'll leave committing optional — maybe commit is nice. I'll just leave files; mention. Actually a quick commit is harmless. Let me check git status/branch.
Write README.
File edits
1 file
WrittenREADME.mdView changes
/sandbox/repo/README.md
Written content · previous contents were not recorded
# Delinea DSV quickstart (Python)Minimal, runnable example against **Delinea DevOps Secrets Vault (DSV)** — Delinea'shosted secrets manager. It uses the official SDK [`python-dsv-sdk`](https://pypi.org/project/python-dsv-sdk/)over plain HTTPS. **No local services, containers, or long-running processes.**## Files| File | Purpose || --- | --- || `dsv_example.py` | Build a vault client, read a secret by path, flatten it into env-style config || `test_dsv_example.py` | Offline test against an in-process fake DSV endpoint (shuts down with the test) || `.env.example` | The four variables you need || `requirements.txt` | `python-dsv-sdk==1.0.4`, `pytest` |## Run the offline test (no account needed)```bashpython -m venv .venv && . .venv/bin/activatepip install -r requirements.txtpytest -q```## Run against a real tenant1. Start a DSV trial/tenant and note the URL, e.g. `https://mytenant.secretsvaultcloud.com`.2. In DSV: **Admin → Clients** → create a client (gives `client_id` / `client_secret`);give its role read access to a secret path.3. Create a secret at `/examples/app/db` with data like `{"username":"app","password":"s3cret"}`.```bashexport DSV_BASE_URL="https://mytenant.secretsvaultcloud.com"export DSV_CLIENT_ID="..."export DSV_CLIENT_SECRET="..."python dsv_example.py /examples/app/db # prints metadata + key namespython dsv_example.py /examples/app/db --show-values```Exit codes: `0` ok, `1` server/transport error, `2` access denied (403/404), `3` missing config.## What the SDK does and does not do- `SecretsVault.get_secret(path)` is **read-only** — enough for the normal app pattern(pull credentials at startup), and the SDK handles token fetch + refresh.- Writes/administration (create/update secrets, roles, clients) are not in this SDK.Use the DSV REST API directly (`POST /v1/secrets` etc.) with an admin-role token,the `dsv` CLI, or the Terraform provider (`DelineaXPM/terraform-provider-dsv`).## Notes- Never log secret values; `to_env_dict()` keeps them in memory only.- Self-signed certs: set `REQUESTS_CA_BUNDLE` rather than disabling verification.- For Delinea **Secret Server** (the self-hosted/hybrid on-prem product) use`python-tss-sdk` instead — different auth model (OAuth2 with API accounts), same shape.
Source: write · chatcmpl-tool-a34636d146ed561e
Recorded tool call
{
"type": "tool_call",
"seq": 69,
"ts": "2026-11-05T16:06:40.655Z",
"name": "write",
"turn": 1,
"input": {
"path": "/sandbox/repo/README.md",
"content": "# Delinea DSV quickstart (Python)\n\nMinimal, runnable example against **Delinea DevOps Secrets Vault (DSV)** — Delinea's\nhosted secrets manager. It uses the official SDK [`python-dsv-sdk`](https://pypi.org/project/python-dsv-sdk/)\nover plain HTTPS. **No local services, containers, or long-running processes.**\n\n## Files\n\n| File | Purpose |\n| --- | --- |\n| `dsv_example.py` | Build a vault client, read a secret by path, flatten it into env-style config |\n| `test_dsv_example.py` | Offline test against an in-process fake DSV endpoint (shuts down with the test) |\n| `.env.example` | The four variables you need |\n| `requirements.txt` | `python-dsv-sdk==1.0.4`, `pytest` |\n\n## Run the offline test (no account needed)\n\n```bash\npython -m venv .venv && . .venv/bin/activate\npip install -r requirements.txt\npytest -q\n```\n\n## Run against a real tenant\n\n1. Start a DSV trial/tenant and note the URL, e.g. `https://mytenant.secretsvaultcloud.com`.\n2. In DSV: **Admin → Clients** → create a client (gives `client_id` / `client_secret`);\n give its role read access to a secret path.\n3. Create a secret at `/examples/app/db` with data like `{\"username\":\"app\",\"password\":\"s3cret\"}`.\n\n```bash\nexport DSV_BASE_URL=\"https://mytenant.secretsvaultcloud.com\"\nexport DSV_CLIENT_ID=\"...\"\nexport DSV_CLIENT_SECRET=\"...\"\npython dsv_example.py /examples/app/db # prints metadata + key names\npython dsv_example.py /examples/app/db --show-values\n```\n\nExit codes: `0` ok, `1` server/transport error, `2` access denied (403/404), `3` missing config.\n\n## What the SDK does and does not do\n\n- `SecretsVault.get_secret(path)` is **read-only** — enough for the normal app pattern\n (pull credentials at startup), and the SDK handles token fetch + refresh.\n- Writes/administration (create/update secrets, roles, clients) are not in this SDK.\n Use the DSV REST API directly (`POST /v1/secrets` etc.) with an admin-role token,\n the `dsv` CLI, or the Terraform provider (`DelineaXPM/terraform-provider-dsv`).\n\n## Notes\n\n- Never log secret values; `to_env_dict()` keeps them in memory only.\n- Self-signed certs: set `REQUESTS_CA_BUNDLE` rather than disabling verification.\n- For Delinea **Secret Server** (the self-hosted/hybrid on-prem product) use\n `python-tss-sdk` instead — different auth model (OAuth2 with API accounts), same shape.\n"
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-a34636d146ed561e",
"tool_kind": "file_edit"
}Recorded tool result
{
"type": "tool_result",
"seq": 70,
"ts": "2026-11-05T16:06:40.655Z",
"turn": 1,
"output": {
"content": [
{
"text": "Successfully wrote 2321 bytes to /sandbox/repo/README.md",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-a34636d146ed561e",
"is_error": false,
"tool_kind": "file_edit"
}›Thinking…
Check .gitignore covers .venv. Then final answer with pricing info and management assessment. Pricing: verify facts I can. Let me quickly check if DSV docs mention any pricing/free limits — one quick search on docs.delinea.com. And Delinea press about pricing model? Let's do a quick check for "Delinea One" / subscription. Keep it to one or two calls, and hedge.
cat /sandbox/repo/.gitignore; timeout 20 curl -sS "https://duckduckgo.com/html/?q=delinea+devops+secrets+vault+pricing+per+secret" 2>/dev/null | python3 -c "
import sys,re,html
t=sys.stdin.read(); t=re.sub(r'<[^>]+>',' ',t); t=html.unescape(t); t=re.sub(r'\s+',' ',t)
i=t.lower().find('pricing'); print(t[:600])" # Keep generated and vendored files out of source diffs.
# Keep generated and vendored files out of source diffs. # Logs / OS cruft *.log .DS_Store # JavaScript / TypeScript node_modules/ .next/ dist/ build/ coverage/ # Python .venv/ venv/ __pycache__/ *.py[cod] .pytest_cache/ *.egg-info/ # Rust target/ # Go / PHP / Ruby vendored deps vendor/ .bundle/ # Java / Kotlin / Scala .gradle/ # .NET bin/ obj/ # Elixir _build/ deps/ # Dart / Flutter .dart_tool/ 302 Found 302 Found nginx
Recorded tool call
{
"type": "tool_call",
"seq": 73,
"ts": "2026-11-05T16:06:43.020Z",
"name": "bash",
"turn": 1,
"input": {
"command": "cat /sandbox/repo/.gitignore; timeout 20 curl -sS \"https://duckduckgo.com/html/?q=delinea+devops+secrets+vault+pricing+per+secret\" 2>/dev/null | python3 -c \"\nimport sys,re,html\nt=sys.stdin.read(); t=re.sub(r'<[^>]+>',' ',t); t=html.unescape(t); t=re.sub(r'\\s+',' ',t)\ni=t.lower().find('pricing'); print(t[:600])\" "
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-aa58c2dbaaaedbdb",
"tool_kind": "shell"
}Recorded tool result
{
"type": "tool_result",
"seq": 74,
"ts": "2026-11-05T16:06:43.896Z",
"turn": 1,
"output": {
"content": [
{
"text": "# Keep generated and vendored files out of source diffs.\n\n# Logs / OS cruft\n*.log\n.DS_Store\n\n# JavaScript / TypeScript\nnode_modules/\n.next/\ndist/\nbuild/\ncoverage/\n\n# Python\n.venv/\nvenv/\n__pycache__/\n*.py[cod]\n.pytest_cache/\n*.egg-info/\n\n# Rust\ntarget/\n\n# Go / PHP / Ruby vendored deps\nvendor/\n.bundle/\n\n# Java / Kotlin / Scala\n.gradle/\n\n# .NET\nbin/\nobj/\n\n# Elixir\n_build/\ndeps/\n\n# Dart / Flutter\n.dart_tool/\n 302 Found 302 Found nginx \n",
"type": "text"
}
]
},
"run_id": "cmw3qlgzo002l0ishiyo2mi9o",
"tool_id": "chatcmpl-tool-aa58c2dbaaaedbdb",
"is_error": false,
"tool_kind": "shell"
}›Thinking…
No search results available (redirect). I'll present pricing honestly: not publicly published; quote-based subscription; free trial; typical units. I won't invent numbers.
Done. Final summary.
Built and verified. Files in /sandbox/repo:
| File | What it is |
|---|---|
dsv_example.py | Reads a secret from Delinea DSV via the official python-dsv-sdk, maps it to env-style config, typed errors + exit codes |
test_dsv_example.py | 6 tests against an in-process fake DSV endpoint (starts/stops inside the test — no service stack). All 6 pass. |
README.md, .env.example, requirements.txt | Setup, run steps, SDK limitations |
What works end-to-end offline: token acquisition → bearer-authenticated secret read → token reuse across calls → config flattening → 403 handling → CLI exit codes.
Where I'm blocked (only one thing): live calls need credentials. There's no self-service API signup I can complete headlessly — a DSV tenant URL plus a client_id/client_secret come from a human trial signup. The hosted API itself is reachable from here (I probed a bogus tenant and got 400 {"message":"Tenant not found"}), so the moment you export DSV_BASE_URL/DSV_CLIENT_ID/DSV_CLIENT_SECRET, python dsv_example.py /examples/app/db hits the real service with no code changes. No long-running commands or local stacks were needed, so nothing else stopped me.
Pricing. Delinea publishes no list prices — I checked the product and /pricing pages and got only "Free Trial / Live Demo," no numbers. So treat this as a quote-driven vendor:
- Sales-led annual subscription, typically priced on subscription tiers + entitlement units (for secrets management: number of secrets and/or consuming identities/seats; for PAM products: privileged users/endpoints).
- A free trial is offered for evaluation; any permanently-free tier would need to be confirmed with sales or in the trial console — I won't guess a limit.
- Budget line items that usually surprise people: DSV vs. Secret Server (hosted vs. hybrid/on-prem) are separate products with separate price points, and platform bundles ("Delinea Platform/One") are negotiated separately.
- Get a written quote that states the unit (per secret? per client identity?), the overage rate, and whether the API/SDK is included at your tier — those are the three things that determine your real cost.
Easy for me to manage? Mostly yes, with one real gap.
- Good: hosted SaaS, one pip package, tiny surface area (
get_secret+ two authorizers), env-var config, clean typed exceptions, offline-testable — I can write, refactor, and test this code confidently without your tenant. - Friction: the Python SDK is read-only. Anything I'd want to do as an operator — create/rotate secrets, manage roles and clients — isn't in it; that needs raw REST calls with an admin token, the
dsvCLI, or the Terraform provider. Also the SDK is thinly maintained (4 releases,python-dsv-sdk), so don't expect feature growth. - Verdict: easy for the app-side pattern (fetch secrets at startup, which is 90% of real usage). Provisioning and lifecycle work will be extra glue code I write against the REST API, and I can't test any of it without credentials.
AX Fundamentals
23 checks. Open an area for details.
This check is a quick preview. Test more tasks and models with Gauge Agents.
Test your product with Gauge Agents