pepr 0.21.1 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +13 -3
  2. package/codecov.yaml +19 -0
  3. package/dist/cli.js +118 -46
  4. package/dist/controller.js +1 -1
  5. package/dist/lib/assets/deploy.d.ts +1 -1
  6. package/dist/lib/assets/deploy.d.ts.map +1 -1
  7. package/dist/lib/assets/index.d.ts +1 -1
  8. package/dist/lib/assets/index.d.ts.map +1 -1
  9. package/dist/lib/assets/pods.d.ts +18 -0
  10. package/dist/lib/assets/pods.d.ts.map +1 -1
  11. package/dist/lib/helpers.d.ts +2 -0
  12. package/dist/lib/helpers.d.ts.map +1 -1
  13. package/package.json +9 -7
  14. package/src/lib/assets/deploy.ts +22 -22
  15. package/src/lib/assets/index.ts +2 -2
  16. package/src/lib/assets/pods.ts +36 -0
  17. package/src/lib/helpers.ts +42 -0
  18. package/website/.linkinator.config.json +0 -8
  19. package/website/.markdownlint.json +0 -6
  20. package/website/.prettierignore +0 -12
  21. package/website/LICENSE +0 -201
  22. package/website/README.md +0 -50
  23. package/website/archetypes/default.md +0 -6
  24. package/website/assets/img/Pepr.Horizontal.white.svg +0 -144
  25. package/website/assets/img/doug.svg +0 -345
  26. package/website/assets/img/pepr.svg +0 -212
  27. package/website/assets/scss/_styles_project.scss +0 -7
  28. package/website/assets/scss/_variables_project.scss +0 -1
  29. package/website/content/en/docs/OnSchedule.md +0 -86
  30. package/website/content/en/docs/_index.md +0 -9
  31. package/website/content/en/docs/cli.md +0 -83
  32. package/website/content/en/docs/codeSample.txt +0 -31
  33. package/website/content/en/docs/concepts.md +0 -238
  34. package/website/content/en/docs/customresources.md +0 -167
  35. package/website/content/en/docs/diagrams.txt +0 -18
  36. package/website/content/en/docs/metrics.md +0 -113
  37. package/website/content/en/docs/rbac.md +0 -153
  38. package/website/content/en/docs/store.md +0 -48
  39. package/website/content/en/docs/webassembly.md +0 -189
  40. package/website/go.mod +0 -8
  41. package/website/go.sum +0 -4
  42. package/website/package-lock.json +0 -3907
  43. package/website/package.json +0 -30
  44. package/website/renovate.json +0 -16
  45. package/website/static/favicons/android-144x144.png +0 -0
  46. package/website/static/favicons/android-192x192.png +0 -0
  47. package/website/static/favicons/android-36x36.png +0 -0
  48. package/website/static/favicons/android-48x48.png +0 -0
  49. package/website/static/favicons/android-72x72.png +0 -0
  50. package/website/static/favicons/android-96x96.png +0 -0
  51. package/website/static/favicons/android-chrome-192x192.png +0 -0
  52. package/website/static/favicons/android-chrome-512x512.png +0 -0
  53. package/website/static/favicons/android-chrome-maskable-192x192.png +0 -0
  54. package/website/static/favicons/android-chrome-maskable-512x512.png +0 -0
  55. package/website/static/favicons/apple-touch-icon-180x180.png +0 -0
  56. package/website/static/favicons/apple-touch-icon.png +0 -0
  57. package/website/static/favicons/favicon-16x16.png +0 -0
  58. package/website/static/favicons/favicon-32x32.png +0 -0
  59. package/website/static/favicons/favicon.ico +0 -0
  60. package/website/static/img/how-to-use.png +0 -0
@@ -1,113 +0,0 @@
1
- ---
2
- title: Metrics
3
- linkTitle: Metrics
4
- ---
5
-
6
- # `/metrics` Endpoint Documentation
7
-
8
- The `/metrics` endpoint provides metrics for the application that are collected via the `MetricsCollector` class. It uses the `prom-client` library and performance hooks from Node.js to gather and expose the metrics data in a format that can be scraped by Prometheus.
9
-
10
- ## Metrics Exposed
11
-
12
- The `MetricsCollector` exposes the following metrics:
13
-
14
- - `pepr_errors`: A counter that increments when an error event occurs in the application.
15
- - `pepr_alerts`: A counter that increments when an alert event is triggered in the application.
16
- - `pepr_Mutate`: A summary that provides the observed durations of mutation events in the application.
17
- - `pepr_Validate`: A summary that provides the observed durations of validation events in the application.
18
-
19
- ## API Details
20
-
21
- **Method:** GET
22
-
23
- **URL:** `/metrics`
24
-
25
- **Response Type:** text/plain
26
-
27
- **Status Codes:**
28
- - 200 OK: On success, returns the current metrics from the application.
29
-
30
- **Response Body:**
31
- The response body is a plain text representation of the metrics data, according to the Prometheus exposition formats. It includes the metrics mentioned above.
32
-
33
- ## Examples
34
-
35
- ### Request
36
-
37
- ```plaintext
38
- GET /metrics
39
- ```
40
-
41
- ### Response
42
- ```plaintext
43
- `# HELP pepr_errors Mutation/Validate errors encountered
44
- # TYPE pepr_errors counter
45
- pepr_errors 5
46
-
47
- # HELP pepr_alerts Mutation/Validate bad api token received
48
- # TYPE pepr_alerts counter
49
- pepr_alerts 10
50
-
51
- # HELP pepr_Mutate Mutation operation summary
52
- # TYPE pepr_Mutate summary
53
- pepr_Mutate{quantile="0.01"} 100.60707900021225
54
- pepr_Mutate{quantile="0.05"} 100.60707900021225
55
- pepr_Mutate{quantile="0.5"} 100.60707900021225
56
- pepr_Mutate{quantile="0.9"} 100.60707900021225
57
- pepr_Mutate{quantile="0.95"} 100.60707900021225
58
- pepr_Mutate{quantile="0.99"} 100.60707900021225
59
- pepr_Mutate{quantile="0.999"} 100.60707900021225
60
- pepr_Mutate_sum 100.60707900021225
61
- pepr_Mutate_count 1
62
-
63
- # HELP pepr_Validate Validation operation summary
64
- # TYPE pepr_Validate summary
65
- pepr_Validate{quantile="0.01"} 201.19413900002837
66
- pepr_Validate{quantile="0.05"} 201.19413900002837
67
- pepr_Validate{quantile="0.5"} 201.2137690000236
68
- pepr_Validate{quantile="0.9"} 201.23339900001884
69
- pepr_Validate{quantile="0.95"} 201.23339900001884
70
- pepr_Validate{quantile="0.99"} 201.23339900001884
71
- pepr_Validate{quantile="0.999"} 201.23339900001884
72
- pepr_Validate_sum 402.4275380000472
73
- pepr_Validate_count 2
74
- ```
75
- ## Prometheus Operator
76
-
77
- If using the Prometheus Operator, the following `ServiceMonitor` example manifests can be used to scrape the `/metrics` endpoint for the `admission` and `watcher` controllers.
78
-
79
- ```yaml
80
- apiVersion: monitoring.coreos.com/v1
81
- kind: ServiceMonitor
82
- metadata:
83
- name: admission
84
- spec:
85
- selector:
86
- matchLabels:
87
- pepr.dev/controller: admission
88
- namespaceSelector:
89
- matchNames:
90
- - pepr-system
91
- endpoints:
92
- - targetPort: 3000
93
- scheme: https
94
- tlsConfig:
95
- insecureSkipVerify: true
96
- ---
97
- apiVersion: monitoring.coreos.com/v1
98
- kind: ServiceMonitor
99
- metadata:
100
- name: watcher
101
- spec:
102
- selector:
103
- matchLabels:
104
- pepr.dev/controller: watcher
105
- namespaceSelector:
106
- matchNames:
107
- - pepr-system
108
- endpoints:
109
- - targetPort: 3000
110
- scheme: https
111
- tlsConfig:
112
- insecureSkipVerify: true
113
- ```
@@ -1,153 +0,0 @@
1
- ---
2
- title: RBAC
3
- linkTitle: RBAC
4
- ---
5
-
6
- # RBAC Modes
7
-
8
- During the build phase of Pepr (`npx pepr build --rbac-mode [admin|scoped]`), you have the option to specify the desired RBAC mode through specific flags. This allows fine-tuning the level of access granted based on requirements and preferences.
9
-
10
- ## Modes
11
-
12
- **admin**
13
-
14
- ```bash
15
- npx pepr build --rbac-mode admin
16
- ```
17
-
18
- **Description:** The service account is given cluster-admin permissions, granting it full, unrestricted access across the entire cluster. This can be useful for administrative tasks where broad permissions are necessary. However, use this mode with caution, as it can pose security risks if misused. This is the default mode.
19
-
20
- **scoped**
21
-
22
- ```bash
23
- npx pepr build --rbac-mode scoped
24
- ```
25
-
26
- **Description:** The service account is provided just enough permissions to perform its required tasks, and no more. This mode is recommended for most use cases as it limits potential attack vectors and aligns with best practices in security. _The admission controller's primary mutating or validating action doesn't require a ClusterRole (as the request is not persisted or executed while passing through admission control), if you have a use case where the admission controller's logic involves reading other Kubernetes resources or taking additional actions beyond just validating, mutating, or watching the incoming request, appropriate RBAC settings should be reflected in the ClusterRole. See how in [Updating the ClusterRole](#updating-the-clusterrole)._
27
-
28
- ## Debugging RBAC Issues
29
-
30
- If encountering unexpected behaviors in Pepr while running in scoped mode, check to see if they are related to RBAC.
31
-
32
- 1. Check Deployment logs for RBAC errors:
33
-
34
- ```bash
35
- kubectl logs -n pepr-system -l app | jq
36
-
37
- # example output
38
- {
39
- "level": 50,
40
- "time": 1697983053758,
41
- "pid": 16,
42
- "hostname": "pepr-static-test-watcher-745d65857d-pndg7",
43
- "data": {
44
- "kind": "Status",
45
- "apiVersion": "v1",
46
- "metadata": {},
47
- "status": "Failure",
48
- "message": "configmaps \"pepr-ssa-demo\" is forbidden: User \"system:serviceaccount:pepr-system:pepr-static-test\" cannot patch resource \"configmaps\" in API group \"\" in the namespace \"pepr-demo-2\"",
49
- "reason": "Forbidden",
50
- "details": {
51
- "name": "pepr-ssa-demo",
52
- "kind": "configmaps"
53
- },
54
- "code": 403
55
- },
56
- "ok": false,
57
- "status": 403,
58
- "statusText": "Forbidden",
59
- "msg": "Dooes the ServiceAccount permissions to CREATE and PATCH this ConfigMap?"
60
- }
61
- ```
62
-
63
- 2. Verify ServiceAccount Permissions with `kubectl auth can-i`
64
-
65
- ```bash
66
- SA=$(kubectl get deploy -n pepr-system -o=jsonpath='{range .items[0]}{.spec.template.spec.serviceAccountName}{"\n"}{end}')
67
-
68
- # Can i create configmaps as the service account in pepr-demo-2?
69
- kubectl auth can-i create cm --as=system:serviceaccount:pepr-system:$SA -n pepr-demo-2
70
-
71
- # example output: no
72
- ```
73
-
74
- 3. Describe the ClusterRole
75
-
76
- ```bash
77
- SA=$(kubectl get deploy -n pepr-system -o=jsonpath='{range .items[0]}{.spec.template.spec.serviceAccountName}{"\n"}{end}')
78
-
79
- kubectl describe clusterrole $SA
80
-
81
- # example output:
82
- Name: pepr-static-test
83
- Labels: <none>
84
- Annotations: <none>
85
- PolicyRule:
86
- Resources Non-Resource URLs Resource Names Verbs
87
- --------- ----------------- -------------- -----
88
- peprstores.pepr.dev [] [] [create delete get list patch update watch]
89
- configmaps [] [] [watch]
90
- namespaces [] [] [watch]
91
- ```
92
-
93
- ## Updating the ClusterRole
94
-
95
- As discussed in the [Modes](#modes) section, the admission controller's primary mutating or validating action doesn't require a ClusterRole (as the request is not persisted or executed while passing through admission control), if you have a use case where the admission controller's logic involves reading other Kubernetes resources or taking additional actions beyond just validating, mutating, or watching the incoming request, appropriate RBAC settings should be reflected in the ClusterRole.
96
-
97
- Step 1: Figure out the desired permissions. (`kubectl create clusterrole --help` is a good place to start figuring out the syntax)
98
-
99
- ```bash
100
- kubectl create clusterrole configMapApplier --verb=create,patch --resource=configmap --dry-run=client -oyaml
101
-
102
- # example output
103
- apiVersion: rbac.authorization.k8s.io/v1
104
- kind: ClusterRole
105
- metadata:
106
- creationTimestamp: null
107
- name: configMapApplier
108
- rules:
109
- - apiGroups:
110
- - ""
111
- resources:
112
- - configmaps
113
- verbs:
114
- - create
115
- - patch
116
- ```
117
-
118
- Step 2: Update the ClusterRole in the `dist` folder.
119
-
120
- ```yaml
121
- ...
122
- apiVersion: rbac.authorization.k8s.io/v1
123
- kind: ClusterRole
124
- metadata:
125
- name: pepr-static-test
126
- rules:
127
- - apiGroups:
128
- - pepr.dev
129
- resources:
130
- - peprstores
131
- verbs:
132
- - create
133
- - get
134
- - patch
135
- - watch
136
- - apiGroups:
137
- - ''
138
- resources:
139
- - namespaces
140
- verbs:
141
- - watch
142
- - apiGroups:
143
- - ''
144
- resources:
145
- - configmaps
146
- verbs:
147
- - watch
148
- - create # New
149
- - patch # New
150
- ...
151
- ```
152
-
153
- Step 3: Apply the updated configuration
@@ -1,48 +0,0 @@
1
- ---
2
- title: Store
3
- linkTitle: Store
4
- ---
5
-
6
- # Pepr Store: A Lightweight Key-Value Store for Pepr Modules
7
-
8
- The nature of admission controllers and general watch operations (the `Mutate`, `Validate` and `Watch` actions in Pepr) make some types of complex and long-running operations difficult. There are also times when you need to share data between different actions. While you could manually create your own K8s resources and manage their cleanup, this can be very hard to track and keep performant at scale.
9
-
10
- The Pepr Store solves this by exposing a simple, [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Storage)-compatible mechanism for use within capabilities. Additionally, as Pepr runs multiple replicas of the admission controller along with a watch controller, the Pepr Store provides a unique way to share data between these different instances automatically.
11
-
12
- Each Pepr Capability has a `Store` instance that can be used to get, set and delete data as well as subscribe to any changes to the Store. Behind the scenes, all capability store instances in a single Pepr Module are stored within a single CRD in the cluster. This CRD is automatically created when the Pepr Module is deployed. Care is taken to make the read and write operations as efficient as possible by using K8s watches, batch processing and patch operations for writes.
13
-
14
- ## Key Features
15
-
16
- - **Asynchronous Key-Value Store**: Provides an asynchronous interface for storing small amounts of data, making it ideal for sharing information between various actions and capabilities.
17
- - **Web Storage API Compatibility**: The store's API is aligned with the standard [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Storage), simplifying the learning curve.
18
- - **Real-time Updates**: The `.subscribe()` and `onReady()` methods enable real-time updates, allowing you to react to changes in the data store instantaneously.
19
-
20
- - **Automatic CRD Management**: Each Pepr Module has its data stored within a single Custom Resource Definition (CRD) that is automatically created upon deployment.
21
- - **Efficient Operations**: Pepr Store uses Kubernetes watches, batch processing, and patch operations to make read and write operations as efficient as possible.
22
-
23
- ## Quick Start
24
-
25
- ```typescript
26
- // Example usage for Pepr Store
27
- Store.setItem("example-1", "was-here");
28
- Store.setItem("example-1-data", JSON.stringify(request.Raw.data));
29
- Store.onReady(data => {
30
- Log.info(data, "Pepr Store Ready");
31
- });
32
- const unsubscribe = Store.subscribe(data => {
33
- Log.info(data, "Pepr Store Updated");
34
- unsubscribe();
35
- });
36
- ```
37
-
38
- ## API Reference
39
-
40
- ### Methods
41
-
42
- - `getItem(key: string)`: Retrieves a value by its key. Returns `null` if the key doesn't exist.
43
- - `setItem(key: string, value: string)`: Sets a value for a given key. Creates a new key-value pair if the key doesn't exist.
44
- - `setItemAndWait(key: string, value: string)`: Sets a value for a given key. Creates a new key-value pair if the key doesn't exist. Returns a promise when the new key and value show up in the store. Should only be used on a `Watch` to avoid [timeouts](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#timeouts).
45
- - `removeItem(key: string)`: Deletes a key-value pair by its key.
46
- - `clear()`: Clears all key-value pairs from the store.
47
- - `subscribe(listener: DataReceiver)`: Subscribes to store updates.
48
- - `onReady(callback: DataReceiver)`: Executes a callback when the store is ready.
@@ -1,189 +0,0 @@
1
- ---
2
- title: WebAssembly
3
- linkTitle: WebAssembly
4
- ---
5
-
6
- # WASM Support: Running WebAssembly in Pepr Guide
7
-
8
- Pepr fully supports WebAssembly. Depending on the language used to generate the WASM, certain files can be too large to fit into a `Secret` or `ConfigMap`. Due to this limitation, users have the ability to incorporate `*.wasm` and any other essential files during the build phase, which are then embedded into the Pepr Controller container. This is achieved through adding an array of files to the `includedFiles` section under `pepr` in the `package.json`.
9
-
10
- > **NOTE -** In order to instantiate the WebAsembly module in TypeScript, you need the WebAssembly type. This is accomplished through add the "DOM" to the `lib` array in the `compilerOptions` section of the `tsconfig.json`. Ex: `"lib": ["ES2022", "DOM"]`. Be aware that adding the DOM will add a lot of extra types to your project and your developer experience will be impacted in terms of the intellisense.
11
-
12
-
13
- ## High-Level Overview
14
-
15
- WASM support is achieved through adding files as layers atop the Pepr controller image, these files are then able to be read by the individual capabilities. The key components of WASM support are:
16
-
17
- - Add files to the **base** of the Pepr module.
18
- - Reference the files in the `includedFiles` section of the `pepr` block of the `package.json`
19
- - Run `npx pepr build` with the `-r ` option specifying registry info. Ex: `npx pepr build -r docker.io/cmwylie19`
20
- - Pepr builds and pushes a custom image that is used in the `Deployment`.
21
-
22
- ## Using WASM Support
23
-
24
- ### Creating a WASM Module in Go
25
-
26
- Create a simple Go function that you want to call from your Pepr module
27
-
28
- ```go
29
- package main
30
-
31
- import (
32
- "fmt"
33
- "syscall/js"
34
- )
35
-
36
- func concats(this js.Value, args []js.Value) interface{} {
37
- fmt.Println("PeprWASM!")
38
- stringOne := args[0].String()
39
- stringTwo := args[1].String()
40
- return fmt.Sprintf("%s%s", stringOne, stringTwo)
41
- }
42
-
43
- func main() {
44
- done := make(chan struct{}, 0)
45
- js.Global().Set("concats", js.FuncOf(concats))
46
- <-done
47
- }
48
- ```
49
-
50
- Compile it to a wasm target and move it to your Pepr module
51
-
52
- ```bash
53
- GOOS=js GOARCH=wasm go build -o main.wasm
54
- cp main.wasm $YOUR_PEPR_MODULE/
55
- ```
56
-
57
- Copy the `wasm_exec.js` from `GOROOT` to your Pepr Module
58
-
59
- ```bash
60
- cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" $YOUR_PEPR_MODULE/
61
- ```
62
-
63
- Update the polyfill to add `globalThis.crypto` in the `wasm_exec.js` since we are not running in the browser. This is needed directly under: `(() => {`
64
-
65
-
66
- ```javascript
67
- // Initialize the polyfill
68
- if (typeof globalThis.crypto === 'undefined') {
69
- globalThis.crypto = {
70
- getRandomValues: (array) => {
71
- for (let i = 0; i < array.length; i++) {
72
- array[i] = Math.floor(Math.random() * 256);
73
- }
74
- },
75
- };
76
- }
77
- ```
78
-
79
-
80
- ### Configure Pepr to use WASM
81
-
82
- After adding the files to the root of the Pepr module, reference those files in the `package.json`:
83
-
84
- ```json
85
- {
86
- "name": "pepr-test-module",
87
- "version": "0.0.1",
88
- "description": "A test module for Pepr",
89
- "keywords": [
90
- "pepr",
91
- "k8s",
92
- "policy-engine",
93
- "pepr-module",
94
- "security"
95
- ],
96
- "engines": {
97
- "node": ">=18.0.0"
98
- },
99
- "pepr": {
100
- "name": "pepr-test-module",
101
- "uuid": "static-test",
102
- "onError": "ignore",
103
- "alwaysIgnore": {
104
- "namespaces": [],
105
- "labels": []
106
- },
107
- "includedFiles":[
108
- "main.wasm",
109
- "wasm_exec.js"
110
- ]
111
- },
112
- ...
113
- }
114
- ```
115
-
116
- Update the `tsconfig.json` to add "DOM" to the `compilerOptions` lib:
117
-
118
- ```json
119
- {
120
- "compilerOptions": {
121
- "allowSyntheticDefaultImports": true,
122
- "declaration": true,
123
- "declarationMap": true,
124
- "emitDeclarationOnly": true,
125
- "esModuleInterop": true,
126
- "lib": [
127
- "ES2022",
128
- "DOM" // <- Add this
129
- ],
130
- "module": "CommonJS",
131
- "moduleResolution": "node",
132
- "outDir": "dist",
133
- "resolveJsonModule": true,
134
- "rootDir": ".",
135
- "strict": false,
136
- "target": "ES2022",
137
- "useUnknownInCatchVariables": false
138
- },
139
- "include": [
140
- "**/*.ts"
141
- ]
142
- }
143
- ```
144
-
145
- ### Call WASM functions from TypeScript
146
-
147
- Import the `wasm_exec.js` in the `pepr.ts`
148
-
149
- ```javascript
150
- import "./wasm_exec.js";
151
- ```
152
-
153
- Create a helper function to load the wasm file in a capability and call it during an event of your choice
154
-
155
- ```typescript
156
- async function callWASM(a,b) {
157
- const go = new globalThis.Go();
158
-
159
- const wasmData = readFileSync("main.wasm");
160
- var concated: string;
161
-
162
- await WebAssembly.instantiate(wasmData, go.importObject).then(wasmModule => {
163
- go.run(wasmModule.instance);
164
-
165
- concated = global.concats(a,b);
166
- });
167
- return concated;
168
- }
169
-
170
- When(a.Pod)
171
- .IsCreated()
172
- .Mutate(async pod => {
173
- try {
174
- let label_value = await callWASM("loves","wasm")
175
- pod.SetLabel("pepr",label_value)
176
- }
177
- catch(err) {
178
- Log.error(err);
179
- }
180
- });
181
- ```
182
-
183
- ### Run Pepr Build
184
-
185
- Build your Pepr module with the registry specified.
186
-
187
- ```bash
188
- npx pepr build -r docker.io/defenseunicorns
189
- ```
package/website/go.mod DELETED
@@ -1,8 +0,0 @@
1
- module main
2
-
3
- go 1.20
4
-
5
- require (
6
- github.com/defenseunicorns/defense-unicorns-hugo-theme v0.3.9 // indirect
7
- github.com/defenseunicorns/defense-unicorns-hugo-theme/dependencies v0.3.9 // indirect
8
- )
package/website/go.sum DELETED
@@ -1,4 +0,0 @@
1
- github.com/defenseunicorns/defense-unicorns-hugo-theme v0.3.9 h1:o2ptq0ozp8x5R61Ik0E08YI3VEBjJGwUvKs8sRleUC8=
2
- github.com/defenseunicorns/defense-unicorns-hugo-theme v0.3.9/go.mod h1:qOBlMoMnovWO8PwmHAlpR7WfPLOJiiq4+XIrVblRb8g=
3
- github.com/defenseunicorns/defense-unicorns-hugo-theme/dependencies v0.3.9 h1:nz4Aiu+ISXKESXgTjPyDyR9L708XjszH6lnQAGFAAVI=
4
- github.com/defenseunicorns/defense-unicorns-hugo-theme/dependencies v0.3.9/go.mod h1:zQT7gnRyPnVCNxREasYkyewPJLhemxlOGZhbu+9mcfQ=