Skip to content

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 gets ON 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 for GRANT/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.