Skip to content

Commit 36f4ea2

Browse files
committed
docs: add documentation site with SEO, OG image, and CI/CD
- Add zensical.toml site config for ClickVault docs - Add SEO generation script using seoslug 2.0.1 (locale, OG image, twitter_site, publisher_logo, emit_warnings, BreadcrumbList) - Add llms.txt, llms-full.txt, robots.txt, glossary.md - Add docs pages: index, quick-start, configuration, roles, architecture, development - Add 1376x768 OG image for Discord embed support - Add clickvault-banner.png to README - Add deploy-docs.yml workflow for GitHub Pages - Add overrides template with seo_html injection
1 parent 7b55f0f commit 36f4ea2

18 files changed

Lines changed: 1839 additions & 0 deletions

‎.github/workflows/deploy-docs.yml‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Deploy docs to GitHub Pages
2+
3+
on:
4+
push:
5+
branches: [master]
6+
paths:
7+
- docs/**
8+
- zensical.toml
9+
- .github/workflows/deploy-docs.yml
10+
workflow_dispatch:
11+
12+
permissions:
13+
contents: read
14+
pages: write
15+
id-token: write
16+
17+
concurrency:
18+
group: pages
19+
cancel-in-progress: true
20+
21+
jobs:
22+
build:
23+
runs-on: ubuntu-latest
24+
steps:
25+
- uses: actions/checkout@v4
26+
- uses: actions/configure-pages@v5
27+
- uses: actions/setup-python@v5
28+
with:
29+
python-version: '3.x'
30+
- run: pip install "seoslug>=2.0.1" zensical
31+
- run: python scripts/generate_seo.py
32+
- run: python scripts/generate_llms_full.py
33+
- run: zensical build --clean
34+
- run: cp docs/robots.txt site/robots.txt
35+
- run: cp docs/llms.txt site/llms.txt
36+
- run: cp docs/llms-full.txt site/llms-full.txt
37+
- uses: actions/upload-pages-artifact@v5
38+
with:
39+
path: site
40+
41+
deploy:
42+
needs: build
43+
runs-on: ubuntu-latest
44+
environment:
45+
name: github-pages
46+
url: ${{ steps.deployment.outputs.page_url }}
47+
steps:
48+
- id: deployment
49+
uses: actions/deploy-pages@v5

‎README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
<p align="center">
2+
<img src="clickvault-banner.png" alt="ClickVault">
3+
</p>
4+
15
# clickvault
26

37
clickvault is a HashiCorp Vault database secrets engine plugin for ClickHouse. It lets Vault create short lived, ephemeral ClickHouse users on demand (dynamic secrets) and rotate the password of long lived ClickHouse users on a schedule (static roles), so applications and operators never handle a standing ClickHouse credential directly.

‎clickvault-banner.png‎

1.05 MB
Loading

‎docs/architecture.md‎

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
---
2+
seo:
3+
title: Architecture - ClickVault Documentation
4+
canonical: https://clickvault.emiliano-go.com/architecture
5+
robots: index,follow
6+
og:
7+
type: website
8+
title: Architecture - ClickVault Documentation
9+
description: Plugin structure
10+
url: https://clickvault.emiliano-go.com/architecture
11+
image: https://clickvault.emiliano-go.com/assets/images/og-image.png
12+
image:width: 1376
13+
image:height: 768
14+
image:alt: ClickVault documentation
15+
site_name: ClickVault Documentation
16+
locale: en_US
17+
twitter:
18+
card: summary_large_image
19+
title: Architecture - ClickVault Documentation
20+
description: Plugin structure
21+
image: https://clickvault.emiliano-go.com/assets/images/og-image.png
22+
image:alt: ClickVault documentation
23+
site: '@emiliano_go_'
24+
description: Plugin structure
25+
schema_jsonld:
26+
- '@context': https://schema.org
27+
'@type': WebPage
28+
name: Architecture - ClickVault Documentation
29+
url: https://clickvault.emiliano-go.com/architecture
30+
description: Plugin structure
31+
image: https://clickvault.emiliano-go.com/assets/images/og-image.png
32+
publisher:
33+
'@type': Organization
34+
name: Emiliano Gandini Outeda
35+
logo: https://clickvault.emiliano-go.com/assets/images/og-image.png
36+
- '@context': https://schema.org
37+
'@type': BreadcrumbList
38+
itemListElement:
39+
- '@type': ListItem
40+
position: 1
41+
name: Architecture
42+
item: https://clickvault.emiliano-go.com/architecture
43+
seo_html: "<title>Architecture - ClickVault Documentation</title>\n<meta name=\"description\"\
44+
\ content=\"Plugin structure\">\n<link rel=\"canonical\" href=\"https://clickvault.emiliano-go.com/architecture\"\
45+
>\n<meta name=\"robots\" content=\"index,follow\">\n<meta property=\"og:type\" content=\"\
46+
website\">\n<meta property=\"og:title\" content=\"Architecture - ClickVault Documentation\"\
47+
>\n<meta property=\"og:description\" content=\"Plugin structure\">\n<meta property=\"\
48+
og:url\" content=\"https://clickvault.emiliano-go.com/architecture\">\n<meta property=\"\
49+
og:image\" content=\"https://clickvault.emiliano-go.com/assets/images/og-image.png\"\
50+
>\n<meta property=\"og:image:width\" content=\"1376\">\n<meta property=\"og:image:height\"\
51+
\ content=\"768\">\n<meta property=\"og:image:alt\" content=\"ClickVault documentation\"\
52+
>\n<meta property=\"og:site_name\" content=\"ClickVault Documentation\">\n<meta\
53+
\ property=\"og:locale\" content=\"en_US\">\n<meta name=\"twitter:card\" content=\"\
54+
summary_large_image\">\n<meta name=\"twitter:title\" content=\"Architecture - ClickVault\
55+
\ Documentation\">\n<meta name=\"twitter:description\" content=\"Plugin structure\"\
56+
>\n<meta name=\"twitter:image\" content=\"https://clickvault.emiliano-go.com/assets/images/og-image.png\"\
57+
>\n<meta name=\"twitter:image:alt\" content=\"ClickVault documentation\">\n<meta\
58+
\ name=\"twitter:site\" content=\"@emiliano_go_\">\n<script type=\"application/ld+json\"\
59+
>\n[\n {\n \"@context\": \"https://schema.org\",\n \"@type\": \"WebPage\"\
60+
,\n \"name\": \"Architecture - ClickVault Documentation\",\n \"url\": \"https://clickvault.emiliano-go.com/architecture\"\
61+
,\n \"description\": \"Plugin structure\",\n \"image\": \"https://clickvault.emiliano-go.com/assets/images/og-image.png\"\
62+
,\n \"publisher\": {\n \"@type\": \"Organization\",\n \"name\": \"\
63+
Emiliano Gandini Outeda\",\n \"logo\": \"https://clickvault.emiliano-go.com/assets/images/og-image.png\"\
64+
\n }\n },\n {\n \"@context\": \"https://schema.org\",\n \"@type\": \"\
65+
BreadcrumbList\",\n \"itemListElement\": [\n {\n \"@type\": \"ListItem\"\
66+
,\n \"position\": 1,\n \"name\": \"Architecture\",\n \"item\"\
67+
: \"https://clickvault.emiliano-go.com/architecture\"\n }\n ]\n }\n]\n\
68+
</script>\n"
69+
---
70+
71+
# Architecture
72+
73+
## Plugin structure
74+
75+
ClickVault implements the Vault database plugin SDK interface
76+
(`sdk/database/dbplugin/v5`). It registers itself with Vault as plugin
77+
type `clickvault` and communicates over the plugin RPC boundary.
78+
79+
```
80+
┌─────────────┐ RPC ┌─────────────────────────────┐
81+
│ Vault │ ◄─────────► │ clickvault plugin │
82+
│ (database │ │ │
83+
│ secrets │ │ ┌───────────────────────┐ │
84+
│ engine) │ │ │ clickvault.go │ │
85+
└─────────────┘ │ │ (6 interface methods)│ │
86+
│ └───────┬───────────────┘ │
87+
│ │ calls │
88+
│ ┌───────▼───────────────┐ │
89+
│ │ ddl.go │ │
90+
│ │ (SQL construction, │ │
91+
│ │ cluster branching) │ │
92+
│ └───────┬───────────────┘ │
93+
│ │ executes │
94+
│ ┌───────▼───────────────┐ │
95+
│ │ ClickHouse server │ │
96+
│ └───────────────────────┘ │
97+
└─────────────────────────────┘
98+
```
99+
100+
## DDL construction
101+
102+
All SQL is built in one place, `internal/clickvault/ddl.go`. The rest of the
103+
plugin never constructs SQL strings itself — it only calls into `ddl.go` and
104+
executes whatever statements come back.
105+
106+
Key design decisions:
107+
108+
- **Cluster handling is centralized.** If the connection is configured with a
109+
`cluster`, every generated statement gets `ON CLUSTER '<cluster>'` appended
110+
automatically (unless the statement already has one). Callers do not branch
111+
on cluster themselves.
112+
113+
- **ON CLUSTER placement follows ClickHouse grammar.** It goes immediately
114+
after the entity name for `CREATE`/`ALTER`/`DROP USER`, and immediately
115+
after the verb for `GRANT`/`REVOKE`.
116+
117+
- **SQL injection prevention.** Generated values are checked for dangerous
118+
characters (quotes, backticks, backslash, control characters) before
119+
substitution.
120+
121+
## Concurrency
122+
123+
`ClickvaultPlugin` holds its ClickHouse connection and config behind a single
124+
`sync.RWMutex`, so one plugin instance is safe for the concurrent calls
125+
Vault's plugin framework makes.
126+
127+
## Error handling
128+
129+
Every error returned from the six interface methods is wrapped with
130+
`fmt.Errorf("clickvault <Method>: %w", err)` so failures are traceable back
131+
to which call produced them.

‎docs/assets/images/og-image.png‎

1.05 MB
Loading

‎docs/configuration.md‎

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
---
2+
seo:
3+
title: Configuration - ClickVault Documentation
4+
canonical: https://clickvault.emiliano-go.com/configuration
5+
robots: index,follow
6+
og:
7+
type: website
8+
title: Configuration - ClickVault Documentation
9+
description: 'Set with vault write database/config/<name pluginname=clickvault
10+
...:'
11+
url: https://clickvault.emiliano-go.com/configuration
12+
image: https://clickvault.emiliano-go.com/assets/images/og-image.png
13+
image:width: 1376
14+
image:height: 768
15+
image:alt: ClickVault documentation
16+
site_name: ClickVault Documentation
17+
locale: en_US
18+
twitter:
19+
card: summary_large_image
20+
title: Configuration - ClickVault Documentation
21+
description: 'Set with vault write database/config/<name pluginname=clickvault
22+
...:'
23+
image: https://clickvault.emiliano-go.com/assets/images/og-image.png
24+
image:alt: ClickVault documentation
25+
site: '@emiliano_go_'
26+
description: 'Set with vault write database/config/<name pluginname=clickvault ...:'
27+
schema_jsonld:
28+
- '@context': https://schema.org
29+
'@type': WebPage
30+
name: Configuration - ClickVault Documentation
31+
url: https://clickvault.emiliano-go.com/configuration
32+
description: 'Set with vault write database/config/<name pluginname=clickvault
33+
...:'
34+
image: https://clickvault.emiliano-go.com/assets/images/og-image.png
35+
publisher:
36+
'@type': Organization
37+
name: Emiliano Gandini Outeda
38+
logo: https://clickvault.emiliano-go.com/assets/images/og-image.png
39+
- '@context': https://schema.org
40+
'@type': BreadcrumbList
41+
itemListElement:
42+
- '@type': ListItem
43+
position: 1
44+
name: Configuration
45+
item: https://clickvault.emiliano-go.com/configuration
46+
seo_html: "<title>Configuration - ClickVault Documentation</title>\n<meta name=\"\
47+
description\" content=\"Set with vault write database/config/&lt;name pluginname=clickvault\
48+
\ ...:\">\n<link rel=\"canonical\" href=\"https://clickvault.emiliano-go.com/configuration\"\
49+
>\n<meta name=\"robots\" content=\"index,follow\">\n<meta property=\"og:type\" content=\"\
50+
website\">\n<meta property=\"og:title\" content=\"Configuration - ClickVault Documentation\"\
51+
>\n<meta property=\"og:description\" content=\"Set with vault write database/config/&lt;name\
52+
\ pluginname=clickvault ...:\">\n<meta property=\"og:url\" content=\"https://clickvault.emiliano-go.com/configuration\"\
53+
>\n<meta property=\"og:image\" content=\"https://clickvault.emiliano-go.com/assets/images/og-image.png\"\
54+
>\n<meta property=\"og:image:width\" content=\"1376\">\n<meta property=\"og:image:height\"\
55+
\ content=\"768\">\n<meta property=\"og:image:alt\" content=\"ClickVault documentation\"\
56+
>\n<meta property=\"og:site_name\" content=\"ClickVault Documentation\">\n<meta\
57+
\ property=\"og:locale\" content=\"en_US\">\n<meta name=\"twitter:card\" content=\"\
58+
summary_large_image\">\n<meta name=\"twitter:title\" content=\"Configuration - ClickVault\
59+
\ Documentation\">\n<meta name=\"twitter:description\" content=\"Set with vault\
60+
\ write database/config/&lt;name pluginname=clickvault ...:\">\n<meta name=\"twitter:image\"\
61+
\ content=\"https://clickvault.emiliano-go.com/assets/images/og-image.png\">\n<meta\
62+
\ name=\"twitter:image:alt\" content=\"ClickVault documentation\">\n<meta name=\"\
63+
twitter:site\" content=\"@emiliano_go_\">\n<script type=\"application/ld+json\"\
64+
>\n[\n {\n \"@context\": \"https://schema.org\",\n \"@type\": \"WebPage\"\
65+
,\n \"name\": \"Configuration - ClickVault Documentation\",\n \"url\": \"\
66+
https://clickvault.emiliano-go.com/configuration\",\n \"description\": \"Set\
67+
\ with vault write database/config/<name pluginname=clickvault ...:\",\n \"image\"\
68+
: \"https://clickvault.emiliano-go.com/assets/images/og-image.png\",\n \"publisher\"\
69+
: {\n \"@type\": \"Organization\",\n \"name\": \"Emiliano Gandini Outeda\"\
70+
,\n \"logo\": \"https://clickvault.emiliano-go.com/assets/images/og-image.png\"\
71+
\n }\n },\n {\n \"@context\": \"https://schema.org\",\n \"@type\": \"\
72+
BreadcrumbList\",\n \"itemListElement\": [\n {\n \"@type\": \"ListItem\"\
73+
,\n \"position\": 1,\n \"name\": \"Configuration\",\n \"item\"\
74+
: \"https://clickvault.emiliano-go.com/configuration\"\n }\n ]\n }\n]\n\
75+
</script>\n"
76+
---
77+
78+
# Configuration
79+
80+
Set with `vault write database/config/<name> plugin_name=clickvault ...`:
81+
82+
| Field | Required | Description |
83+
|---|---|---|
84+
| `connection_url` | yes | ClickHouse address, e.g. `clickhouse://host:9000`. A scheme is required. |
85+
| `username` | yes | The Vault admin user in ClickHouse. Must have `ACCESS MANAGEMENT` grant. |
86+
| `password` | yes | Password for `username`. |
87+
| `cluster` | no | If set, all DDL statements get `ON CLUSTER '<cluster>'` appended. |
88+
| `username_template` | no | Go template for dynamic usernames. See below for default. |
89+
90+
Pass `verify_connection=true` (the default) to have `Initialize` ping ClickHouse
91+
and confirm the admin user has the `ACCESS MANAGEMENT` privilege before
92+
accepting the config.
93+
94+
## Username template
95+
96+
Dynamic usernames are generated with `sdk/helper/template`. The default
97+
template is:
98+
99+
```text
100+
{{ printf "v-%s-%s-%s-%s" (.DisplayName | truncate 64) (.RoleName | truncate 64) (random 8) (unix_time) | truncate 255 }}
101+
```
102+
103+
This produces names like `v-token-myrole-a1b2c3d4-1719945600`. ClickHouse
104+
identifiers are limited to 255 characters. You can override this with
105+
`username_template` in the connection config.
106+
107+
## Password policy
108+
109+
ClickHouse's `sha256_password` auth type has no hard character set requirement,
110+
but Vault needs a password policy:
111+
112+
```hcl
113+
length = 20
114+
rule "charset" {
115+
charset = "abcdefghijklmnopqrstuvwxyz"
116+
}
117+
rule "charset" {
118+
charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
119+
min-chars = 1
120+
}
121+
rule "charset" {
122+
charset = "0123456789"
123+
min-chars = 1
124+
}
125+
```
126+
127+
`scripts/setup_vault.sh` creates this policy as `clickhouse-password-policy`.

0 commit comments

Comments
 (0)