Skip to content
Gauge

ax-check.com

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.

Onboarding

Give agents a clear login handoff and a way to resume setup.

Agents cannot progress through onboarding without human help. Provide an agent-accessible path through authentication and setup.

Docs

Return Markdown when clients request text/markdown.

Homepage returned text/html for a text/markdown request; no Markdown representation offered.

Docs

List every offered API, MCP, and skills surface in /llms.txt.

llms.txt mentions MCP glossary and API blog but no API/MCP/skills developer surfaces.

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.

piv0.84.2
modelQwen 3.8 Maxcwd/sandbox/repo
Help me build a simple example using Delinea. Tell me how pricing works, and briefly tell me whether this product will be easy for you to manage. Let me know if you get blocked. If this product has no developer workflow you can act on, say so plainly and stop. Stay light: use the hosted product through its SDK or API. Do not start local service stacks or wait for long-running commands; if the quickstart requires either, say so plainly and stop.
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
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
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:

  • dsv CLI (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's python-sdk repo: 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 -V
200
200
301
---
Python 3.12.3
pip 26.2.1 from /opt/freestyle/python/lib/python3.12/site-packages/pip (python 3.12)
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 
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,
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"}
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
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

![PyPI](https://img.shields.io/pypi/v/python-dsv-sdk?style=for-the-badge)
![License](https://img.shields.io/github/license/DelineaXPM/python-dsv-sdk?style=for-the-badge)
![Python Versions](https://img.shields.io/pypi/pyversions/python-dsv-sdk?style=for-the-badge)
![PyPI - Downloads](https://img.shields.io/pypi/dm/python-dsv-sdk?style=for-the-badge)

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
```
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-sdk in a venv (quick, no service stack).
  • Write dsv_example.py that 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 responses or a simple http.server in 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']
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
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])
"; done
404
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
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

Completed
Writtenrequirements.txtView changes

/sandbox/repo/requirements.txt

Written content · previous contents were not recorded

python-dsv-sdk==1.0.4
pytest>=7.0

File edits

1 file

Completed
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.com
DSV_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 read
DSV_SECRET_PATH=/examples/app/db
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

Completed
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 a
remote 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 annotations
import argparse
import json
import os
import sys
from 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 and
the 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 and
are 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 2
except SecretsVaultError as exc:
print(f"DSV call failed: {exc}", file=sys.stderr)
return 1
except ValueError as exc:
print(f"configuration error: {exc}", file=sys.stderr)
return 3
config = 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 0
if __name__ == "__main__":
raise SystemExit(main())
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

Completed
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 (token
request -> bearer-authenticated secret read -> config mapping) is correct before
you point the same code at a real tenant.
"""
from __future__ import annotations
import json
import threading
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import pytest
import dsv_example
from delinea.secrets.vault import SecretsVaultAccessError
SECRET_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 = 0
def log_message(self, *args): # silence request logging
pass
def _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 += 1
self._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 reused
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
def 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"]) == 2
assert "access denied" in capsys.readouterr().err
def 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"]) == 0
out = capsys.readouterr().out
assert "DB_PASSWORD" in out
assert "s3cret" not in out # values hidden unless --show-values
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
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

Completed
Modifiedtest_dsv_example.py+1−0View changes

/sandbox/repo/test_dsv_example.py

Replacement excerpt

def tenant():
+ FakeDsv.tokens_issued = 0
server = ThreadingHTTPServer
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"}
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

Completed
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's
hosted 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)
```bash
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
pytest -q
```
## Run against a real tenant
1. 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"}`.
```bash
export 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 names
python 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.
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 
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:

FileWhat it is
dsv_example.pyReads a secret from Delinea DSV via the official python-dsv-sdk, maps it to env-style config, typed errors + exit codes
test_dsv_example.py6 tests against an in-process fake DSV endpoint (starts/stops inside the test — no service stack). All 6 pass.
README.md, .env.example, requirements.txtSetup, 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 dsv CLI, 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

AX Check by Gauge