hashicorp/vault
Patterns and conventions
Reading Vault becomes much easier once you know its house style. These are the patterns that recur in nearly every package.
File suffixes
| Suffix | Meaning |
|---|---|
_ce.go |
Open-source (Community Edition) stub. Replaced by _ent.go in Enterprise builds. |
_oss.go |
Same idea, older convention. |
_util.go |
Helper functions for the file with the same prefix. |
_stubs_oss.go |
Empty stub functions that Enterprise overrides. Don't delete them. |
_testing.go, _testing_util.go |
Helpers for tests in the same package. |
_test.go |
Standard Go test file. |
_enumer.go |
Generated by dmarkham/enumer. Don't hand-edit; regenerate via go generate. |
The entExtend... and entAdd... function names follow the same OSS/Enterprise split. If you find yourself writing one, follow the existing pattern: an OSS file with func entExtendFoo() {} and an Enterprise file with the real body.
Backends use framework
Almost every auth/secret backend is a *framework.Backend constructed with a list of *framework.Path entries. The pattern (e.g., from builtin/logical/transit/backend.go):
func Backend(conf *logical.BackendConfig) *backend {
var b backend
b.Backend = &framework.Backend{
Help: "...",
PathsSpecial: &logical.Paths{...},
Paths: framework.PathAppend(b.paths(), b.batchPaths(), ...),
Secrets: []*framework.Secret{b.secret()},
BackendType: logical.TypeLogical,
}
return &b
}Each *framework.Path has a regex Pattern, a Fields map, and Operations/Callbacks that handle CRUD. The framework auto-generates OpenAPI specs, help text, and capability checks.
Logging
- Use the package-scoped
hclog.Loggerinjected viaBackendConfig.Loggerorcore.logger. Don't calllog.Println. - Levels:
Traceis fine-grained tracing,Debugis per-request,Infois lifecycle events,Warnis a recoverable problem,Erroris something operators should investigate. - Log keys are camelCase:
b.logger.Info("rotated key", "mount", mountPath, "version", version). - Don't log secrets. Audit devices already record requests; don't duplicate.
Errors
- Wrap with
fmt.Errorf("...: %w", err)soerrors.Isworks. - Surface user-friendly messages by returning a
logical.ErrorResponse(...)for 4xx-class errors; reserve actual Goerrors for internal/5xx-class problems. - Status mapping happens in
sdk/logical/response_util.go— read it before adding new error categories.
Locking
vault.Core uses an RWMutex for the seal/unseal lifecycle. Most subsystems own their own mutex. Some patterns to know:
Core.stateLock(invault/core.go) — guards the unseal state. Take it read-locked for normal operations; write-locked only during seal/unseal/HA transitions.Mounts.lock(invault/mount.go) — guards the mount table.ExpirationManager.pendingLock— guards the leases map.tokenStoreuses fine-grained locks per token to avoid contention.
The codebase intentionally uses helper/locking/ wrappers in some hot paths to instrument lock waits.
Request handling
Every HTTP request flows through http/handler.go → vault.HandleRequest (in vault/request_handling.go) → router → backend. The contract is:
- Inputs:
*logical.Request(path, operation, data, namespace, client info). - Outputs:
*logical.Response(data, secret, auth, warnings) pluserror.
If a backend returns a Secret, the expiration manager will register a lease. If it returns an Auth, the token store will mint a token.
Backends should never reach into vault.Core. They get a system view (logical.SystemView) for limited access to mount info, namespace, and license features.
Tests with TestCluster
vault.TestCluster (and vault.NewTestCluster) instantiates a 3-node in-memory cluster. Use it whenever you need real HA / forwarding behavior; use raw vault.TestCoreUnsealed for single-node, no-forwarding tests.
OSS-vs-Enterprise build tags
Build tags appear at the top of file:
//go:build !enterprise
package vaultThe OSS tree is built with -tags= empty; Enterprise sets -tags=enterprise. Don't add new tags without consulting the team — the matrix is already complex.
Generated code
These files are generated and checked in. Never edit them by hand:
*.pb.goand*.pb.goclient/*_grpc.pb.go(regenerated bymake proto).*_enumer.go(go generate).http/web_ui/(UI bundle, regenerated bymake ember-dist).helper/builtinplugins/registry_full.gois hand-maintained but lists every Enterprise-built plugin.
Copywrite
Every file has a // Copyright IBM Corp. 2016, 2025 line followed by // SPDX-License-Identifier: BUSL-1.1. The copywrite tool (.copywrite.hcl) verifies and inserts these. Run copywrite headers --plan to preview, copywrite headers to apply. CI fails if a file is missing the header.
Sensitive data
- Never write tokens, passwords, or unwrap responses to stdout/stderr.
- The audit broker HMACs sensitive fields based on
audit/headers.goandhelper/identity/allow-lists. - For tests, use
t.Setenvort.TempDirso secrets don't leak between tests.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.