Skip to content

Commit 39cc3cc

Browse files
author
Josh Calder
authored
Add Telemetry to keystone dev (#8118)
1 parent 7f664fd commit 39cc3cc

File tree

34 files changed

+966
-8
lines changed

34 files changed

+966
-8
lines changed

.changeset/metal-tips-invite.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@keystone-6/core': minor
3+
'@keystone-6/fields-document': patch
4+
---
5+
6+
Adds Project and Device Telemetry events to the `keystone dev` script

docs/components/docs/Navigation.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,9 @@ export function DocsNavigation() {
287287
Query Filters <Badge look="success">Updated</Badge>
288288
</NavItem>
289289
</NavSection>
290+
<NavSection title="Reference">
291+
<NavItem href="/docs/reference/telemetry">Telemetry</NavItem>
292+
</NavSection>
290293
</nav>
291294
// </NavContextProvider>
292295
);

docs/markdoc/config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ function validateLink(node: Node, config: Config): ValidationError[] {
215215
if (
216216
/https?:\/\//.test(link) ||
217217
// local # is validated in the document validation
218-
link.startsWith('#')
218+
link.startsWith('#') ||
219+
link.startsWith('mailto:')
219220
) {
220221
return [];
221222
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
---
2+
title: "Privacy Policy (Telemetry)"
3+
description: "Keystone collects anonymous telemetry events when running `keystone dev` - Learn what is sent, what the information is used for, and how to opt-out"
4+
---
5+
6+
7+
Keystone 6 is software developed and maintained by Thinkmill Labs Pty Ltd and the open source community, and this privacy policy is on behalf of Thinkmill Labs and in relation to Keystone, and to the extent applicable, similar software under the Keystone GitHub organisation.
8+
9+
This page describes how we collect and hold **anonymous** telemetry data, the rationale for why we are collecting this information, when it is collected, and how **you can opt-out**. Unless you opt out, you consent to us collecting, holding, analysing and using the telemetry data in accordance with this policy, which may be updated from time to time.
10+
11+
Please note that Keystone currently uses [NextJS](https://nextjs.org/) and [Prisma](https://www.prisma.io/) as software dependencies, and each of these projects include their own telemetry with their own respective privacy policies.
12+
You can learn more about them by visiting the following:
13+
14+
- [https://nextjs.org/telemetry](https://nextjs.org/telemetry)
15+
- [https://www.prisma.io/docs/concepts/more/telemetry](https://www.prisma.io/docs/concepts/more/telemetry)
16+
17+
---
18+
19+
## Why do we collect and hold telemetry?
20+
21+
Keystone only collects telemetry that we reasonably require for the continued development of the software, and, to the extent applicable, we do so in accordance with the Australian Privacy Principles.
22+
23+
Telemetry helps us learn about how Keystone is being used by developers and projects that may not be actively participating on our GitHub, Twitter and Slack.
24+
The questions we want to currently answer using telemetry are the following:
25+
26+
- How many active developers are using Keystone, compared to how many are active in the community,
27+
- How complex are projects that use Keystone,
28+
- What field types and `@keystone-6` or community packages are being used by developers, and
29+
- What operating systems and node versions are being used by the community?
30+
31+
This information will help Thinkmill Labs prioritise future features or maintenance efforts for ongoing development.
32+
33+
---
34+
35+
## When is telemetry collected?
36+
37+
Keystone only collects telemetry when you, the developer, explicitly start the software using `keystone dev`, or by using the `cli` function with an `argv` value of `dev`, as exposed by our `@keystone-6/core/scripts/cli` export.
38+
39+
There are four scenarios when running `keystone dev`, where we **do not collect** telemetry:
40+
41+
- The first time running `keystone dev`, when displaying the telemetry notice, or
42+
- If the `CI` environment variable is set (to anything), or
43+
- If the `NODE_ENV` environment variable is set to `"production"`, or
44+
- If you have opted out - see [How to opt-out below](#how-to-opt-out)
45+
46+
In any of these scenarios, telemetry will be disabled and prevented from sending any information.
47+
48+
---
49+
50+
## What information is collected?
51+
52+
{% hint kind="warn" %}
53+
⚠️ The exact structure and contents of the telemetry information collected may be subject to change when the software is updated.
54+
Developers should watch [our releases on GitHub](https://github.com/keystonejs/keystone/releases) for any changes in what telemetry information is reported by the Software.
55+
Any material increase in the amount of information collected will result in us re-notifying developers of that change the next time they run `keystone dev`; and updating this page.
56+
57+
{% /hint %}
58+
59+
Keystone collects telemetry information in the form of two different types of data reports:
60+
61+
- Information about the device running `keystone dev`, and
62+
- Information about the project’s configuration
63+
64+
We refer to these two different reports, as “device telemetry” and “project telemetry” respectively.
65+
66+
These reports are forwarded to [https://telemetry.keystonejs.com/](https://telemetry.keystonejs.com/), and are reported separately to minimize any correlation between them insofar as the timing and grouping of that data, that an otherwise combined report may have. We are collecting these two reports for different reasons, and thus have no need to associate them.
67+
68+
We additionally record a timestamp of the time that the report is received by the server at [https://telemetry.keystonejs.com](https://telemetry.keystonejs.com/).
69+
70+
**Device Telemetry**
71+
The type of information contained within a device telemetry report is currently:
72+
73+
- The last date you used `keystone dev`, and
74+
- The node major version number, and
75+
- The name of your operating system
76+
77+
A device telemetry report is formatted as JSON and currently looks like:
78+
79+
```json
80+
{
81+
"previous": "2022-11-23",
82+
"os": "darwin",
83+
"node": "18"
84+
}
85+
```
86+
87+
**Project Telemetry**
88+
89+
The type of information contained within a project telemetry report is currently:
90+
91+
- The last date you used `keystone dev` for this project, and
92+
- The resolved versions of any `@keystone-6` packages used by this project, and
93+
- The number of lists for this project, and
94+
- The name and number of field types that you are using
95+
96+
A project telemetry report is formatted as JSON and currently looks like:
97+
98+
```json
99+
{
100+
"previous": "2022-11-23",
101+
"versions": {
102+
"@keystone-6/auth": "5.0.1",
103+
"@keystone-6/core": "3.1.2",
104+
"@keystone-6/document-renderer": "1.1.2",
105+
"@keystone-6/fields-document": "5.0.2"
106+
},
107+
"lists": 3,
108+
"fields": {
109+
"unknown": 1,
110+
"@keystone-6/text": 5,
111+
"@keystone-6/image": 1,
112+
"@keystone-6/file": 1
113+
}
114+
}
115+
```
116+
117+
---
118+
119+
## Will we share this information?
120+
121+
The telemetry information as collected will be used and analysed by Thinkmill Labs to answer the aforementioned questions. For these purposes, we might use 3rd party services.
122+
123+
Where possible, we want to share this information with the open-source community surrounding Keystone. Thinkmill Labs respects your privacy, and as such, any information that is shared will only be shared in an aggregate form.
124+
125+
We do not sell this data, and we don’t intend to.
126+
127+
Thinkmill Labs will make its best effort to remove any outliers, even in aggregate form, that could be used to identify a particular device or project.
128+
129+
---
130+
131+
## How long will we keep this information?
132+
133+
Thinkmill Labs will only keep this information until it can be reduced down to aggregate form to answer the aforementioned questions.
134+
For that purpose, **we currently only retain telemetry reports and their respective timestamps for 1 year**. After that period, singular telemetry reports are deleted and only the analysed, aggregate form of the respective data is retained.
135+
136+
---
137+
138+
## How to opt-out?
139+
140+
To opt-out of Keystone device and project telemetry for your user profile, in 1 command, you can use the following:
141+
142+
```bash
143+
$ keystone telemetry disable
144+
```
145+
146+
If you change your user profile on your device, you will need to run this command again.
147+
Alternatively, to disable telemetry for your project in a way that is compatible with your source control, add the following to your project's `keystone.ts` configuration file:
148+
149+
```tsx
150+
export default config({
151+
db: {
152+
// ...
153+
},
154+
lists,
155+
156+
// this will opt-out of device and project telemetry, for this project
157+
telemetry: false
158+
})
159+
```
160+
161+
If you want to reset your telemetry configuration for your user profile, you can use `keystone telemetry reset`.
162+
If you want to opt-in to keystone telemetry for your user profile, you can use `keystone telemetry enable`.
163+
164+
Keystone stores your telemetry preferences in a location defined by [env-paths](https://github.com/sindresorhus/env-paths#pathsconfig), an open source library, which currently stores the data in the following locations:
165+
166+
| Operating System | Location |
167+
| --- | --- |
168+
| MacOS | ~/Library/Preferences/keystonejs |
169+
| Linux | ~/.config/keystonejs (or $XDG_CONFIG_HOME/keystonejs) |
170+
| Windows | %APPDATA%\keystonejs\Config (for example C:\Users\YOUR_USERNAME\AppData\Roaming\keystonejs\Config) |
171+
172+
**Network-wide opt-out**
173+
174+
If you have a network-wide firewall, you can opt-out of Keystone telemetry by not resolving the following domain: [telemetry.keystonejs.com](https://telemetry.keystonejs.com)
175+
176+
---
177+
178+
## How can I see what is currently configured
179+
180+
If you wish to see how telemetry is currently configured for your device or project, you can run `keystone telemetry status`.
181+
182+
## What if I have a complaint or question
183+
184+
If you have any questions or concerns about the information that is gathered please contact us by logging a GitHub Issue [https://github.com/keystonejs/keystone](https://github.com/keystonejs/keystone).
185+
186+
Alternatively please contact our Privacy Officer by email to [[email protected]](mailto:[email protected]), or by mail to Level 10, 191 Clarence Street, Sydney NSW 2000.
187+
188+
For further information about Keystone’s security policy please see [https://github.com/keystonejs/keystone/security/policy](https://github.com/keystonejs/keystone/security/policy)

docs/redirects.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,12 @@ const CURRENT = [
232232
destination: '/docs/graphql/overview',
233233
permanent: false,
234234
},
235-
235+
/* Telemetry - used to shorten the URL for CLI message */
236+
{
237+
source: '/telemetry',
238+
destination: '/docs/reference/telemetry',
239+
permanent: true,
240+
},
236241
/* Move updates to blog posts */
237242
{
238243
source: '/updates/general-availability',

packages/core/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,9 @@
265265
"bcryptjs": "^2.4.3",
266266
"bytes": "^3.1.1",
267267
"chalk": "^4.1.2",
268+
"ci-info": "^3.2.0",
268269
"clipboard-copy": "^4.0.1",
270+
"conf": "^10.0.3",
269271
"cookie": "^0.5.0",
270272
"cors": "^2.8.5",
271273
"cuid": "^2.1.8",
@@ -289,6 +291,7 @@
289291
"meow": "^9.0.0",
290292
"micro": "^9.3.4",
291293
"next": "^13.0.3",
294+
"node-fetch": "^2.6.7",
292295
"p-limit": "^2.3.0",
293296
"pluralize": "^8.0.0",
294297
"prisma": "4.3.1",

packages/core/src/fields/types/bigInt/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ export const bigInt =
172172
output: graphql.field({
173173
type: config.graphql?.read?.isNonNull ? graphql.nonNull(graphql.BigInt) : graphql.BigInt,
174174
}),
175+
__ksTelemetryFieldTypeName: '@keystone-6/bigInt',
175176
views: '@keystone-6/core/fields/types/bigInt/views',
176177
getAdminMeta() {
177178
return {

packages/core/src/fields/types/calendarDay/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ export const calendarDay =
142142
return value;
143143
},
144144
}),
145+
__ksTelemetryFieldTypeName: '@keystone-6/calendarDay',
145146
views: '@keystone-6/core/fields/types/calendarDay/views',
146147
getAdminMeta(): CalendarDayFieldMeta {
147148
return {

packages/core/src/fields/types/checkbox/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export const checkbox =
7171
output: graphql.field({
7272
type: config.graphql?.read?.isNonNull ? graphql.nonNull(graphql.Boolean) : graphql.Boolean,
7373
}),
74+
__ksTelemetryFieldTypeName: '@keystone-6/checkbox',
7475
views: '@keystone-6/core/fields/types/checkbox/views',
7576
getAdminMeta: () => ({ defaultValue }),
7677
});

packages/core/src/fields/types/decimal/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ export const decimal =
182182
return val;
183183
},
184184
}),
185+
__ksTelemetryFieldTypeName: '@keystone-6/decimal',
185186
views: '@keystone-6/core/fields/types/decimal/views',
186187
getAdminMeta: (): import('./views').DecimalFieldMeta => ({
187188
defaultValue: defaultValue ?? null,

packages/core/src/fields/types/file/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export const file =
114114
return { filename, filesize, storage: config.storage };
115115
},
116116
}),
117+
__ksTelemetryFieldTypeName: '@keystone-6/file',
117118
views: '@keystone-6/core/fields/types/file/views',
118119
});
119120
};

packages/core/src/fields/types/float/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ export const float =
159159
output: graphql.field({
160160
type: config.graphql?.read?.isNonNull ? graphql.nonNull(graphql.Float) : graphql.Float,
161161
}),
162+
__ksTelemetryFieldTypeName: '@keystone-6/float',
162163
views: '@keystone-6/core/fields/types/float/views',
163164
getAdminMeta() {
164165
return {

packages/core/src/fields/types/image/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ export const image =
151151
};
152152
},
153153
}),
154+
__ksTelemetryFieldTypeName: '@keystone-6/image',
154155
views: '@keystone-6/core/fields/types/image/views',
155156
});
156157
};

packages/core/src/fields/types/integer/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ export const integer =
184184
output: graphql.field({
185185
type: config.graphql?.read?.isNonNull ? graphql.nonNull(graphql.Int) : graphql.Int,
186186
}),
187+
__ksTelemetryFieldTypeName: '@keystone-6/integer',
187188
views: '@keystone-6/core/fields/types/integer/views',
188189
getAdminMeta() {
189190
return {

packages/core/src/fields/types/json/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const json =
2727
meta.provider,
2828
{
2929
...config,
30+
__ksTelemetryFieldTypeName: '@keystone-6/json',
3031
input: {
3132
create: {
3233
arg: graphql.arg({ type: graphql.JSON }),

packages/core/src/fields/types/multiselect/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export const multiselect =
106106
meta.provider,
107107
{
108108
...config,
109+
__ksTelemetryFieldTypeName: '@keystone-6/multiselect',
109110
hooks: {
110111
...config.hooks,
111112
async validateInput(args) {

packages/core/src/fields/types/password/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ export const password =
183183
resolve: inputResolver,
184184
},
185185
},
186+
__ksTelemetryFieldTypeName: '@keystone-6/password',
186187
views: '@keystone-6/core/fields/types/password/views',
187188
getAdminMeta: (): PasswordFieldMeta => ({
188189
isNullable,

packages/core/src/fields/types/relationship/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ export const relationship =
9898

9999
const commonConfig = {
100100
...config,
101+
__ksTelemetryFieldTypeName: '@keystone-6/relationship',
101102
views: '@keystone-6/core/fields/types/relationship/views',
102103
getAdminMeta: (): Parameters<typeof import('./views').controller>[0]['fieldMeta'] => {
103104
const adminMetaRoot = getAdminMetaForRelationshipField();

packages/core/src/fields/types/select/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export const select =
8282
const commonConfig = (
8383
options: readonly { value: string | number; label: string }[]
8484
): CommonFieldConfig<ListTypeInfo> & {
85+
__ksTelemetryFieldTypeName: string;
8586
views: string;
8687
getAdminMeta: () => import('./views').AdminSelectFieldMeta;
8788
} => {
@@ -110,6 +111,7 @@ export const select =
110111
await config.hooks?.validateInput?.(args);
111112
},
112113
},
114+
__ksTelemetryFieldTypeName: '@keystone-6/select',
113115
views: '@keystone-6/core/fields/types/select/views',
114116
getAdminMeta: () => ({
115117
options,

packages/core/src/fields/types/text/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ export const text =
177177
output: graphql.field({
178178
type: config.graphql?.read?.isNonNull ? graphql.nonNull(graphql.String) : graphql.String,
179179
}),
180+
__ksTelemetryFieldTypeName: '@keystone-6/text',
180181
views: '@keystone-6/core/fields/types/text/views',
181182
getAdminMeta(): TextFieldMeta {
182183
return {

packages/core/src/fields/types/timestamp/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export const timestamp =
135135
? graphql.nonNull(graphql.DateTime)
136136
: graphql.DateTime,
137137
}),
138+
__ksTelemetryFieldTypeName: '@keystone-6/timestamp',
138139
views: '@keystone-6/core/fields/types/timestamp/views',
139140
getAdminMeta(): TimestampFieldMeta {
140141
return {

packages/core/src/fields/types/virtual/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export const virtual =
8484
return usableField.resolve!(item as any, ...args);
8585
},
8686
}),
87+
__ksTelemetryFieldTypeName: '@keystone-6/virtual',
8788
views: '@keystone-6/core/fields/types/virtual/views',
8889
getAdminMeta: () => ({ query: config.ui?.query || '' }),
8990
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export const defaults = {
22
healthCheckPath: '/_healthcheck',
3+
telemetryEndpoint: 'https://telemetry.keystonejs.com',
34
} as const;

0 commit comments

Comments
 (0)