Skip to content
Talk to our solutions team

Vault Overview

kis Vault (vault.svc) is the platform’s trust plane. It is one service with three pillars:

  1. Secrets engine: tenant-scoped, encrypted-at-rest, audit-logged secret CRUD.
  2. Zero-trust mTLS PKI: issuance, rotation, and revocation of short-lived X.509 service identities from the internal certificate authority (authority kisai), plus storage of externally-issued certificates.
  3. Service-to-service JWTs: short-lived, Vault-signed JWTs replace long-lived API keys for outbound service calls.

The division of labor with IAM: IAM issues user-facing tokens; Vault custodies keys. IAM mints access tokens from key material it pulls from Vault; Vault owns the service identity registry (SPIFFE IDs) and the signing keys.

ComponentWhat it is
vault.svcThe server. Default port :7999, metrics on 127.0.0.1:9098.
vault-agentPer-node daemon. Attests local workloads (process credentials, binary path/hash), caches credentials, and serves them over the Unix socket /run/vault/vault.sock.
Vault client (SDK)Every platform service embeds a vault client with two transports: remote (HTTPS direct to vault.svc) or agent (via the local socket). Selected by config: vault.client.transport.
kis vaultCLI: developer commands ride the local agent; operator commands use an mTLS operator certificate.

Workload identity follows the SPIFFE model: every service and node gets a spiffe://kis.ai/... identity encoded in its certificate. Node certificates live 24 hours and auto-renew at half-life; service certificates default to 90 days.

Two orthogonal choices decide where the sensitive material actually lives. Both are pure configuration:

  • Storage backend: where secret values are stored. Supported: hcpvault (HashiCorp Vault KV-v2, the default in production), openbao (wire-compatible), azurekv and awssm (preview), and fsvault (plaintext YAML on disk, development only; the server refuses to run it with env: prod).
  • Signer: what holds the CA/JWT signing keys. Supported: inproc (key on disk, default), pkcs11 (HSM), awskms, azurekv-keys, gcpkms, the private key never leaves the signer, Vault only ever asks it to sign.

There is no Shamir-style unseal of Vault itself. The root of trust is the CA material (on disk for inproc, inside the HSM/KMS for custody signers) plus the storage backend’s own unseal.

On top of the backend’s native at-rest encryption, Vault can add a per-tenant envelope: secrets are AES-GCM-256 encrypted with a tenant-specific key before they ever reach the backend, with the secret’s identity bound in as associated data, ciphertext cannot be copied between records or tenants. It is off by default (vault.envelope.enabled); existing plaintext secrets keep working on read, and only new writes are wrapped.

Vault is tenant-scoped along the platform’s CEPT hierarchy (customer / environment / product / tenant). Secrets exist at three scopes, encoded as name prefixes when a workload asks for them:

PrefixScope
vault:NAMETenant scope. The caller’s full CEPT
vaultcluster:NAMECluster scope (datacenter + cluster)
vaultkey:NAMEGlobal scope

For tenant-scoped operations, the authoritative scope is always the propagated caller identity (the tenancy context on the request), never whatever CEPT a request body claims. This closes the confused-deputy hole: a service cannot read another tenant’s secret by naming that tenant in its payload. The same rule covers certificates: a request that names a tenant’s realm signing key, or any other certificate scoped to a tenant, must act for that tenant. Enforcement is deny-by-default (vault.enforce_caller_cept: true) and fails closed; a write is refused on a mismatch whatever the setting.

Vault keeps every secret and certificate record in its own datastore. The tenant a record belongs to is part of the record, so a cluster-scoped certificate or secret is found whichever tenant the caller acts for.

Vault is also tenant-deepest by design: there are no per-user secrets. Per-user material is application-layer envelope encryption using a tenant key that is itself held in Vault.

Platform services do not call the HTTP API directly, the chassis wires a vault client in at boot:

  • Configuration values can reference secrets: a config value of the form vault:db-password is resolved through the vault client (ResolveSecret) when the service reads it, with the caller’s CEPT applied. vaultcluster: and vaultkey: work the same way at their scopes.
  • The client is the chassis vault client (lib-chassis), registered per service and returned by ResolveVaultClient(serviceName). Its API covers secrets (get/put/has/delete), certificates (get/issue/put, and the issuer CA), and service tokens (IssueServiceToken(ctx, audience)), which receivers verify against Vault’s JWKS.
  • Caching: on the remote transport, secret reads are cached for one minute, certificates for one hour and the issuer CA for 24 hours. A service token is renewed at half its lifetime, and at least hourly. When Vault cannot be reached, the client serves a secret from its last good read for up to vault.client.max_stale (default 24h; 0 or off turns this off) and keeps using a held service token until it expires. A “not found” or “denied” answer from Vault takes effect at once.
  • Moving a service from direct HTTPS to the node agent is a one-line, per-service, reversible config flip: vault.client.transport: remote → agent.
Path / portPurpose
:7999vault.svc API listener (mTLS)
127.0.0.1:9098Server metrics (OpenTelemetry/Prometheus)
/run/vault/vault.sockAgent workload socket
127.0.0.1:9099Agent metrics
/etc/vault/vault.yamlServer bootstrap config
/etc/vault/agent.yamlAgent config
/var/lib/vaultAgent state directory

Continue with: