Point Versus at the secrets manager you already run — HashiCorp Vault, CyberArk, or AWS Secrets Manager — so managed-device credentials are fetched on demand and never stored in Versus, resolved either in the cloud or in-network on a collector.
Not every organization wants device credentials living in a new place. If you already run HashiCorp Vault, CyberArk, or AWS Secrets Manager, Versus can read a managed device's credential straight from your existing secrets manager at connection time — so the credential is never stored in Versus. Versus keeps only the coordinates: which vault to talk to, and the path where the secret lives.
This is distinct from the Versus Vault, which stores secrets in Versus with envelope encryption. External Vault Integrations store nothing secret — they are a pointer plus an on-demand resolver. Use them when your policy says credentials must stay in the system of record you already operate.
Two records model an integration:
auth_ref), never stored in the connection.secret_path and which fields hold the username and password. A device is bound to a reference; nothing about the secret's value is persisted.When a workflow, discovery, or a device operation needs the credential, Versus (or a collector) resolves the reference on demand, uses it, and discards it. A resolved credential is cached in memory only, for a short TTL, and is never written to Postgres, never written to the Versus Vault, and never logged.
The feature is inert until you create a connection, and it rides the Secure Vault module entitlement — the same one that gates the Versus Vault. See Security & Compliance.
| Provider | Key | Status |
|---|---|---|
| HashiCorp Vault (KV v1/v2) | hashicorp | Implemented |
| CyberArk CCP / AIM | cyberark_ccp | Implemented |
| AWS Secrets Manager | aws_sm | Implemented |
| CyberArk Conjur | conjur | Roadmap |
| Azure Key Vault | azure_kv | Roadmap |
| GCP Secret Manager | gcp_sm | Roadmap |
| Delinea Secret Server | delinea | Roadmap |
The roadmap providers are declared in the model so the UI and API accept them, but only HashiCorp, CyberArk CCP, and AWS Secrets Manager have working backends today. Selecting an unimplemented provider fails closed with a clear "not implemented yet" error rather than silently doing nothing.
Because the three secrets managers are shaped differently, the connection's generic fields are reused per provider:
| Field | HashiCorp | CyberArk CCP | AWS Secrets Manager |
|---|---|---|---|
address | https://vault.corp:8200 | CCP base URL | (unused) |
namespace | Enterprise namespace | AppID | AWS region |
mount | KV engine mount | Safe | (unused) |
auth_ref | Vault token or {role_id, secret_id} | optional client-cert PEM (mTLS) | optional {aws_access_key_id, …} |
secret_path (on the reference) | KV path | Object name | secret name or ARN |
For HashiCorp, auth_method is token or AppRole in the current release. For AWS, leaving auth_ref blank uses the ambient IAM role (task, instance, or IRSA) — the recommended path, so no static keys are stored anywhere. For CyberArk, the application is authorized by AppID plus allowed-machine/IP, with an optional client certificate for mutual TLS.
Versus reads a device's credential through a single facade. When a device is bound to an external secret reference, that path takes precedence over the Versus Vault enrollment and the legacy credential field — and it is fail-closed: if the external vault is unreachable or the path is wrong, the operation fails rather than falling back to a stale or different credential.
device operation needs credentials
│
▼
device bound to an external secret? ──no──▶ Versus Vault enrollment / legacy credential
│ yes
▼
resolve on the collector? ──yes──▶ dispatch carries only the path;
│ no the collector reads it in-network (Mode C)
▼
cloud resolves via the provider ▶ TTL-cached in memory ▶ used ▶ discarded
A short cache TTL (default 60 seconds, per reference) means a rotation in your external vault is picked up automatically on the next connection — Versus never holds a credential long enough to go stale.
resolve_atWhere the secret is read is the most important decision, because it determines whether the plaintext ever touches Versus:
cloud — Versus resolves the secret in the application core. Suitable when the external vault is reachable from Versus (public endpoint, PrivateLink, etc.).collector — the secret is read in your network by a collector, so it never traverses Versus. The dispatch to the collector carries only the non-secret KV path; the collector's local resolver reads it from your vault using the collector's own identity.auto (default) — resolve on the collector when the device is bound to a site (and therefore reached through a collector), otherwise resolve in the cloud.When a reference is collector-resolved, a stray attempt to resolve it cloud-side raises an error instead of quietly connecting with a legacy credential — the fail-closed guarantee holds in both directions.
Collector-side resolution is the collector plane's Mode C. A collector can resolve credentials in three modes, and one collector can run several at once:
| Mode | Where the secret lives | Used for |
|---|---|---|
| A | Sealed on the collector's local disk (AES-256-GCM) | Versus-managed credentials kept in-network |
| B | Delivered decrypted inside the signed dispatch envelope | Cloud-held credentials, in memory only |
| C | Read live from your external vault, in-network | External Vault Integrations |
A CompositeResolver routes each credential request to the right mode by tenant, provider, and target, so a single collector can inject an F5 credential via Mode A while resolving another device's credential from your HashiCorp Vault via Mode C — neither path sees the other's secrets. The collector's HashiCorp backend authenticates with AppRole (role_id/secret_id → short-lived client token, cached until just before expiry), reads the KV v2 path, and re-composes the exact {username, password} blob a provider driver expects — so the driver cannot tell whether the bytes came from the local sealed vault or your external one. Every mode still enforces its own scope: composition never widens access. See Collector Security and Collector Fleet.
If your HashiCorp Vault exposes a dynamic-secret engine (database, LDAP, etc.), mark the reference dynamic and give it a dynamic_role (for example database/creds/f5-lab). Each resolution then mints ephemeral credentials and a lease — never cached — and the lease id rides back with the result. When a Privileged Session Management broker closes a session, it revokes the lease, so the credential is dead after use. This is rotate-on-close: the device credential a session used is invalidated the moment the session ends.
POST /api/extvault/connections/
Authorization: Bearer eyJhbGc…
Content-Type: application/json
{
"name": "Corp HashiCorp Vault",
"provider": "hashicorp",
"address": "https://vault.corp.example:8200",
"mount": "secret",
"kv_version": 2,
"auth_method": "approle",
"auth_ref": "vault://root/extvault/corp-hashicorp/approle",
"resolve_at": "auto",
"network": "collector_lan",
"tls_verify": true
}
auth_ref is a vault:// reference into the Versus Vault, where the bootstrap token or {role_id, secret_id} JSON is stored — it is write-only from the API's point of view and never returned. network records how the vault is reached (public, privatelink, or collector_lan).
POST /api/extvault/connections/{id}/test/
This authenticates and runs a cheap reachability check, updating the connection's state (draft → active, or error) and last_ok_at / last_error. It never returns secret material — only { "ok": true, "detail": "authenticated to … (mount=secret)" }.
POST /api/extvault/secrets/
Content-Type: application/json
{
"connection": "…connection-id…",
"name": "F5 lab pair",
"secret_path": "network/f5/aegon-lab",
"username_key": "username",
"password_key": "password",
"cache_ttl_s": 60
}
username_key / password_key name the fields inside the stored secret (defaulting to username / password). Pin a specific KV v2 version with secret_version, or leave it blank for the latest. Set dynamic: true with a dynamic_role for a dynamic engine.
POST /api/extvault/secrets/{id}/test/
Resolves the secret once to confirm the path and keys are correct. The response returns the username (a username is not itself the secret) and a boolean password_found — it never echoes the password:
200 OK
{
"ok": true,
"username": "svc-f5-lab",
"password_found": true,
"detail": "resolved network/f5/aegon-lab (username='svc-f5-lab', password present)"
}
Set the device's credential source to the reference. From then on, every device operation — discovery, F5 BIG-IP automation, certificate deployment, a workflow step — resolves the credential on demand, subject to resolve_at. You can also bind a connection to a collector so its Mode-C policy is configured to match.
| Stored in Versus | Never stored in Versus |
|---|---|
| Connection metadata (address, mount, auth method) | The external vault's bootstrap secret (kept as a Versus Vault reference) |
| The secret's path and field names | The device credential value (resolved on demand, memory-cached only) |
| Resolution audit events (who / what / where / outcome) | Dynamic-secret leases (minted per use, never cached) |
Every resolution emits a secret-free audit event — connection, provider, path, purpose, and success/failure — and, where available, records it on the Versus Vault's tamper-evident hash-chained audit trail. Auditing is best-effort and never breaks resolution. Connections and references are tenant-scoped with row-level security, so one tenant can never see or resolve another's.
External Vault Integrations sit alongside the Versus Vault as an alternative credential source, chosen per device. The rest of the platform is unaware of the difference: providers, workflows, and certificate lifecycle ask for a device's credential the same way regardless of where it comes from. Combined with collector-side (Mode C) resolution, a bank or regulated enterprise can automate its estate with Versus while keeping every privileged credential inside its own secrets manager and network. See Platform Architecture for the layers involved.