Skip to content

Commit 28be66d

Browse files
authored
Merge pull request #104 from mapbox/telemetry-cli-token-first
Send telemetry with the CLI's own token only
2 parents 44f3b85 + 470bca1 commit 28be66d

11 files changed

Lines changed: 1754 additions & 1471 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,25 @@ that may never merge. They are not releases and are not listed here.
3131
`cli_token_unavailable` and `cli_token_forbidden` (see
3232
[Errors](docs/commands.md#errors)).
3333

34-
- The CLI's own token, used for telemetry when you have no token of your
35-
own, is now fetched from `https://cli.mapbox.com/config/token.json` and
36-
kept in `~/.mapbox/cli-token.json` instead of being built into the binary,
34+
- The CLI's own token, used for telemetry, is now fetched from
35+
`https://cli.mapbox.com/config/token.json` and kept in
36+
`~/.mapbox/cli-token.json` instead of being built into the binary,
3737
so it can be rotated without a release. What changes for an existing
3838
setup:
3939
- `MAPBOX_CLI_BUNDLED_TOKEN` is no longer read at build time. A build
4040
from source fetches the token like a release does.
4141
- Keeping the token creates `~/.mapbox` if it is missing, even on a run
4242
whose only network use is telemetry.
43-
- When an API refuses the token, the Tokens API is asked why. Only a
44-
revoked, invalid, expired or malformed token is replaced, at once.
45-
Three fetches in a row that find no usable token, or three refusals
46-
in a row of a valid one, pause that for an hour.
43+
- When an API refuses the token, the Tokens API is asked why and the
44+
endpoint asked for a new token at once. A revoked, invalid, expired or
45+
malformed token is replaced; a different token served is used from the
46+
next run on.
47+
- Three failures in a row, each a fetch that finds no usable token or a
48+
refusal with no different token served, stop telemetry sending and
49+
fetching for an hour. The count is telemetry's own: it never holds
50+
`mapbox report` or `MAPBOX_CLI_TOKEN`, and the waits an earlier dev
51+
build kept in `cli-token.json` are ignored. A new token ends it, even
52+
one `mapbox report` fetched.
4753

4854
- `tilesets query` renders as a numbered list under `-o text` again, as
4955
documented. It had printed pretty JSON since 0.2.0, when the command moved
@@ -305,13 +311,14 @@ that may never merge. They are not releases and are not listed here.
305311
file, so with two installs, set it again after using the older one.
306312

307313
- Each run sends one `cli.command` telemetry event to Mapbox, from a
308-
background process the command doesn't wait for, with your own token
309-
(`--token`, `MAPBOX_ACCESS_TOKEN` or your login) or, when you have none,
310-
one built into the CLI. A build from source has no built-in token, so with
311-
no token of your own the event is dropped. It never touches stdout, the
312-
exit code or how long a command takes, so a script sees no difference; a
313-
machine that already has `~/.mapbox` finds a `.telemetry` directory in it.
314-
`MAPBOX_CLI_NO_TELEMETRY=1` turns it off.
314+
background process the command doesn't wait for. It is only ever sent
315+
with the CLI's own public token, never yours (`--token`,
316+
`MAPBOX_ACCESS_TOKEN` or your login), since Mapbox Events keeps the token
317+
an event was sent with. When the CLI's token can't be used, or the send
318+
fails, the event is dropped; it is never sent twice. It never touches
319+
stdout, the exit code or how long a command takes, so a script sees no
320+
difference; a machine that already has `~/.mapbox` finds a `.telemetry`
321+
directory in it. `MAPBOX_CLI_NO_TELEMETRY=1` turns it off.
315322

316323
- `MAPBOX_CLI_EXTRA_QUERY` appends raw query parameters to every request, in
317324
the same `k1=v1&k2=v2` shape as a URL's own query string — for an API

‎README.md‎

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -301,11 +301,10 @@ Each run also sends one event to Mapbox:
301301
It never includes a file path or free text you typed. It carries a random ID
302302
that is replaced on the first run a day after it was created. It is sent in
303303
the background, so a command never waits for it, and dropped if it can't be
304-
delivered. It is sent with your own token (`--token`, `MAPBOX_ACCESS_TOKEN`
305-
or your login) when you have one, and with the CLI's own public token
306-
otherwise; Mapbox Events keeps the token an event was sent with, and the
307-
account it belongs to, alongside the event. With no token of your own and
308-
no way to fetch the CLI's, the event is dropped.
304+
delivered. It is only ever sent with the CLI's own public token, never
305+
yours (`--token`, `MAPBOX_ACCESS_TOKEN` or your login); Mapbox Events keeps
306+
the token an event was sent with alongside the event. When the CLI's token
307+
can't be used, the event is dropped, and it is never sent twice.
309308
`MAPBOX_CLI_NO_TELEMETRY=1` or `mapbox config set telemetry off` turns it off.
310309

311310
### Diagnostics and settings
@@ -491,22 +490,27 @@ and `--dry-run` show it alongside everything else on the request.
491490

492491
### The CLI's own requests
493492

494-
The CLI has a token of its own, for requests it makes for itself. Telemetry
495-
uses your own token whenever you have one: `--token` or
496-
`MAPBOX_ACCESS_TOKEN`, then your login. Only when you have none does it use
497-
the CLI's token:
498-
`MAPBOX_CLI_TOKEN`, or a public token fetched from
499-
`https://cli.mapbox.com/config/token.json` and kept in
500-
`~/.mapbox/cli-token.json`. It is replaced only when the Tokens API says it
501-
is revoked, invalid, expired or malformed, not when one API refuses it. A
502-
refusal of a valid token, or one the Tokens API cannot explain, counts; the
503-
third in a row pauses telemetry's sending with it for an hour, as three
504-
fetches in a row that find no usable token pause fetching. The token is
505-
restricted to `https://cli.mapbox.com/`, so every request with it carries
506-
that as its `Referer`. The CLI's token is only used for what
507-
`src/cli_token.rs` lists for it: telemetry, and `mapbox report`, which is
508-
the other way around: it prefers the CLI's token to yours and falls back to
509-
yours while the CLI's can't be used.
493+
The CLI has a token of its own, for requests it makes for itself.
494+
Telemetry uses only that token, never the one you run commands with
495+
(`--token`, `MAPBOX_ACCESS_TOKEN` or a login): it uses a public token
496+
fetched from `https://cli.mapbox.com/config/token.json` and kept in
497+
`~/.mapbox/cli-token.json`. `MAPBOX_CLI_TOKEN` is a developer override that
498+
replaces that token with another CLI token, such as one for staging, and
499+
is used only when it is a public `pk.` token. With neither usable, the
500+
event is dropped, and an event that was refused is never sent again. A
501+
refusal is checked with the Tokens API and the endpoint asked for a new
502+
token at once: a token that is revoked, invalid, expired or malformed is
503+
replaced, and a different token served is used from the next run on.
504+
Three failures in a row, each a fetch that finds no usable token or a
505+
refusal the endpoint has no different token for, stop telemetry sending
506+
and fetching for an hour. That count is telemetry's own, and
507+
`MAPBOX_CLI_TOKEN` is sent whatever it says. A new token ends it, even one
508+
`mapbox report` fetched. The token is restricted to
509+
`https://cli.mapbox.com/`, so every request with it carries that as its
510+
`Referer`. The CLI's token is only used for what
511+
`src/cli_token.rs` lists for it: telemetry, and `mapbox report`, which
512+
prefers the CLI's token and falls back to yours while the CLI's can't be
513+
used.
510514

511515
### Proxies
512516

@@ -663,7 +667,7 @@ tools are used and how to improve our products.
663667
`--dry-run` or whether a named profile was used), how it ended and how
664668
long it took, how many requests it made with their HTTP status and sizes,
665669
the kind of token it used and the Mapbox account that token belongs to,
666-
the Mapbox access token the event itself was sent with, a random
670+
the CLI's own public token the event itself was sent with, a random
667671
identifier that is replaced every day, which run started this one when it
668672
ran inside another, CLI version, how the CLI was installed, the version
669673
it was updated from and any newer one it offered, OS/architecture,
@@ -679,8 +683,8 @@ tools are used and how to improve our products.
679683
adoption, prioritize investments, and improve the reliability,
680684
performance, and developer experience of our CLIs.
681685
- **What We Do Not Collect:** Code completion outputs, source code, project
682-
file names, directory contents, non-Mapbox API keys, or credentials other
683-
than the Mapbox access token an event is sent with.
686+
file names, directory contents, non-Mapbox API keys, or credentials,
687+
your Mapbox access token included.
684688
- **Who has Access:** Telemetry data will not be disclosed to, or accessed
685689
by, third parties other than Mapbox affiliates and passive cloud storage
686690
and hosting providers necessary to maintain our infrastructure.

‎docs/architecture.md‎

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ flowchart LR
191191
events["Mapbox Events<br/>POST /events/v2"]
192192
end
193193
194-
bin -- "① spawn detached, event + token<br/>on stdin (never argv or env)" --> child
194+
bin -- "① spawn detached, event<br/>on stdin (never argv or env)" --> child
195195
child -- "② 🔒 HTTPS POST" --> events
196196
```
197197

@@ -203,15 +203,18 @@ entirely, same as Diagram 2.
203203

204204
Step ① is detached and non-blocking: the parent spawns a copy of itself in
205205
this special mode and exits without waiting on it, so a dead network or a
206-
slow endpoint never adds to a command's run time. The event and the token it
207-
travels with go over the child's stdin rather than its argv or environment,
208-
for the same `ps`-visibility reason Diagram 3 gives.
209-
210-
Step ②'s token follows `src/cli_token.rs`'s fallback order: the user's own
211-
token first (flag, environment, or stored login), and only when none of
212-
those exist, the CLI's `pk.` token as a last resort so usage from a user who
213-
has never logged in is still counted. Every build fetches that token from
214-
`https://cli.mapbox.com/config/token.json` and keeps it in
206+
slow endpoint never adds to a command's run time. The event goes over the
207+
child's stdin rather than its argv or environment, for the same
208+
`ps`-visibility reason Diagram 3 gives. The child is not handed the user's
209+
token at all.
210+
211+
Step ② is sent with the CLI's own `pk.` token only, never the user's
212+
(`src/cli_token.rs`): `MAPBOX_CLI_TOKEN` when set to a `pk.` token,
213+
otherwise the one every build fetches from
214+
`https://cli.mapbox.com/config/token.json` and keeps in
215215
`~/.mapbox/cli-token.json` until the Tokens API says it is dead
216-
(`src/cli_token/fetch.rs`). With no login and no way to fetch it, the event
217-
is simply dropped.
216+
(`src/cli_token/fetch.rs`). With no usable CLI token the event is dropped,
217+
and a send that fails is not tried again. Three failures in a row (a fetch
218+
with no usable token, or a refusal with no different token served) quiet
219+
telemetry for an hour, a count kept per caller in that same file. A new
220+
token saved by any caller ends every caller's count.

‎src/auth.rs‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -479,8 +479,7 @@ fn credentials_path_readonly(profile: Option<&str>) -> Option<PathBuf> {
479479
/// directory as a side effect of reading it — see
480480
/// [`credentials_path_readonly`]. What [`profiles`] reads each stored
481481
/// profile through, since listing what exists must not be the reason a
482-
/// directory starts to exist or its permissions change, and what
483-
/// [`crate::cli_token`] reads the login through for the same reason.
482+
/// directory starts to exist or its permissions change.
484483
pub(crate) fn load_credentials_readonly(profile: Option<&str>) -> Option<Credentials> {
485484
let data = std::fs::read_to_string(credentials_path_readonly(profile)?).ok()?;
486485
serde_json::from_str(&data).ok()
@@ -718,7 +717,7 @@ pub fn warn_if_environment_token_shadows_login(profile: Option<&str>, remedy: &s
718717
}
719718
}
720719

721-
pub(crate) fn token_needs_refresh(token: &str) -> bool {
720+
fn token_needs_refresh(token: &str) -> bool {
722721
match token_expires_at(token) {
723722
Some(exp) => {
724723
let now = std::time::SystemTime::now()
@@ -1068,8 +1067,8 @@ pub fn logout(profile: Option<&str>, mode: Mode) -> Result<()> {
10681067
let path = credentials_path(profile)?;
10691068
let had_credentials = path.exists();
10701069
if had_credentials {
1071-
// Waits out a refresh in flight, such as the telemetry sender's from
1072-
// the previous run, which would otherwise write the login back.
1070+
// Wait for another run's login refresh to finish, so it can't write
1071+
// the login back after it is removed.
10731072
let _lock = CredentialLock::acquire(profile).ok();
10741073
crate::run_record::set_auth_step("remove_credentials");
10751074
std::fs::remove_file(&path)?;

0 commit comments

Comments
 (0)