Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions docs/UPDATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ Installer scripts download the matching release asset for the local platform and
verify its `.sha256` file. If Zero is already installed, run `zero upgrade`
instead of reinstalling.

## Authentication

Unauthenticated GitHub API requests are subject to strict
[rate limits](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api).
If you see `403 Forbidden` during update checks, set a GitHub personal access token:

| Environment variable | Role |
|---|---|
| `ZERO_GITHUB_TOKEN` | Used for update checks (takes precedence) |
| `GITHUB_TOKEN` | Fallback when `ZERO_GITHUB_TOKEN` is not set |

Note: `GITHUB_TOKEN` is also used by the GitHub Models provider
(`internal/providercatalog/catalog.go:150`); both recipients are GitHub so no
trust boundary is crossed, but a token set for the provider will also be sent
on update checks.

Tokens are only sent when the request URL is `https://api.github.com`. Custom
endpoints (set via `--endpoint` or `ZERO_UPDATE_RELEASE_URL`) and plain HTTP
URLs never receive credentials. Redirects that downgrade to `http` are refused,
so the bearer token is never sent in cleartext (Go's standard library would
otherwise copy `Authorization` on same-host redirects based on host alone).

## Windows recovery state (standalone installs)

This section describes Windows only. On Linux and macOS a standalone update
Expand Down
19 changes: 18 additions & 1 deletion internal/cli/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1523,11 +1523,28 @@
if exitCode != exitSuccess {
t.Fatalf("expected exit code %d, got %d: %s", exitSuccess, exitCode, stderr.String())
}
for _, want := range []string{"--check", "--repo", "--endpoint", "--timeout", "--target"} {
for _, want := range []string{"--check", "--repo", "--endpoint", "--timeout", "--target", "ZERO_GITHUB_TOKEN", "ZERO_UPDATE_RELEASE_URL"} {
if !strings.Contains(stdout.String(), want) {
t.Fatalf("expected update help to document %s, got %q", want, stdout.String())
}
}
// GITHUB_TOKEN must appear as a standalone token, not as a substring of ZERO_GITHUB_TOKEN.
out := stdout.String()
if idx := strings.Index(out, "GITHUB_TOKEN"); idx < 0 {
t.Fatalf("expected update help to document GITHUB_TOKEN, got %q", out)
} else if idx >= 5 && out[idx-5:idx] == "ZERO_" {
// Check after the ZERO_GITHUB_TOKEN occurrence too.
rest := out[idx+len("GITHUB_TOKEN"):]
if strings.Index(rest, "GITHUB_TOKEN") < 0 {

Check failure on line 1538 in internal/cli/app_test.go

View workflow job for this annotation

GitHub Actions / Security & code health

S1003: should use !strings.Contains(rest, "GITHUB_TOKEN") instead (staticcheck)

Check failure on line 1538 in internal/cli/app_test.go

View workflow job for this annotation

GitHub Actions / Smoke (windows-latest)

S1003: should use !strings.Contains(rest, "GITHUB_TOKEN") instead (staticcheck)
// Only ZERO_GITHUB_TOKEN matched; GITHUB_TOKEN is missing as a standalone entry.
// Find the first occurrence for the error message.
firstIdx := strings.Index(out, "ZERO_GITHUB_TOKEN")
after := out[firstIdx+len("ZERO_GITHUB_TOKEN"):]
if strings.Index(after, "GITHUB_TOKEN") < 0 {

Check failure on line 1543 in internal/cli/app_test.go

View workflow job for this annotation

GitHub Actions / Security & code health

S1003: should use !strings.Contains(after, "GITHUB_TOKEN") instead (staticcheck)

Check failure on line 1543 in internal/cli/app_test.go

View workflow job for this annotation

GitHub Actions / Smoke (windows-latest)

S1003: should use !strings.Contains(after, "GITHUB_TOKEN") instead (staticcheck)
t.Fatalf("expected update help to document GITHUB_TOKEN (standalone), got %q", out)
}
}
}
if stderr.Len() != 0 {
t.Fatalf("expected empty stderr, got %q", stderr.String())
}
Expand Down
7 changes: 7 additions & 0 deletions internal/cli/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,13 @@ Flags:
--timeout <duration> Release check timeout (default 5s)
--target <platform> Release target to verify with --check (for example windows-x64); not valid with --apply
-h, --help Show this help

Environment:
ZERO_GITHUB_TOKEN Token for update checks (takes precedence over GITHUB_TOKEN)
Only sent to https://api.github.com over HTTPS; never sent to custom endpoints
or on redirect to plain HTTP
GITHUB_TOKEN Fallback token for update checks
ZERO_UPDATE_RELEASE_URL Override the release API URL (same as --endpoint)
`)
return err
}
Loading
Loading