Architecture¶
Plugin structure¶
ClickVault implements the Vault database plugin SDK interface
(sdk/database/dbplugin/v5). It registers itself with Vault as plugin
type clickvault and communicates over the plugin RPC boundary.
┌─────────────┐ RPC ┌─────────────────────────────┐
│ Vault │ ◄─────────► │ clickvault plugin │
│ (database │ │ │
│ secrets │ │ ┌───────────────────────┐ │
│ engine) │ │ │ clickvault.go │ │
└─────────────┘ │ │ (6 interface methods)│ │
│ └───────┬───────────────┘ │
│ │ calls │
│ ┌───────▼───────────────┐ │
│ │ ddl.go │ │
│ │ (SQL construction, │ │
│ │ cluster branching) │ │
│ └───────┬───────────────┘ │
│ │ executes │
│ ┌───────▼───────────────┐ │
│ │ ClickHouse server │ │
│ └───────────────────────┘ │
└─────────────────────────────┘
DDL construction¶
All SQL is built in one place, internal/clickvault/ddl.go. The rest of the
plugin never constructs SQL strings itself; it only calls into ddl.go and
executes whatever statements come back.
Key design decisions:
-
Cluster handling is centralized. If the connection is configured with a
cluster, every generated statement getsON CLUSTER '<cluster>'inserted at the grammatically correct position automatically (unless the statement already has one). Callers do not branch on cluster themselves. -
ON CLUSTER placement follows ClickHouse grammar. It goes immediately after the entity name for
CREATE/ALTER/DROP USER, and immediately after the verb forGRANT/REVOKE. -
SQL injection prevention. Generated values are checked for dangerous characters (quotes, backticks, backslash, control characters) before substitution.
Concurrency¶
ClickvaultPlugin holds its ClickHouse connection and config behind a single
sync.RWMutex, so one plugin instance is safe for the concurrent calls
Vault's plugin framework makes.
Error handling¶
Every error returned from the six interface methods is wrapped with
fmt.Errorf("clickvault <Method>: %w", err) so failures are traceable back
to which call produced them.