Compare commits

5 Commits

Author SHA1 Message Date
lerko d076fa6b26 ci(release): skip GitHub relay when GH_MIRROR_TOKEN absent
CI / test (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
CI / vulncheck (pull_request) Has been cancelled
Relay is on hold until after the rc dress rehearsal — without the
secret the step exits cleanly instead of failing the release run.
Adding the secret later enables it with no workflow change.
2026-06-12 15:08:11 -04:00
lerko 5145237e88 test(importer): cover malformed Kuma backup input
Importer parses untrusted JSON on the migration onboarding path with no
coverage. Add malformed-input table (truncated, wrong types, null
lists), notification config edge cases, and field-mapping checks.
2026-06-12 14:30:48 -04:00
lerko fc8cbcafa5 docs(monitor): document before-Start contract on engine setters 2026-06-12 14:30:48 -04:00
lerko 6df9b52033 fix(tui): apply log filter to full log list, not viewport window
viewLogsTab filtered logViewport.View() — the visible window — so the
entry count showed the window size and hidden lines reappeared while
scrolling. Filter and render now happen at content-set time from
engine.GetLogs(); the view only reads stored counts.
2026-06-12 14:30:48 -04:00
lerko 49f419896c ci(release): relay release artifacts to GitHub mirror
GoReleaser publishes to exactly one SCM (Gitea); the push mirror carries
refs but not releases, so GitHub Releases — where the README points —
stayed empty. After the Gitea release, wait for the mirrored tag and
create the GitHub release with the same artifacts and notes.

Needs new Gitea secret GH_MIRROR_TOKEN (GitHub PAT with repo scope).
GITHUB_TOKEN is reserved by Gitea Actions, hence the different name.
2026-06-12 14:30:48 -04:00
7 changed files with 10 additions and 10 deletions
+1 -1
View File
@@ -174,7 +174,7 @@ Export your Kuma backup JSON, then:
```bash
curl -X POST http://localhost:8080/api/import/kuma \
-H "X-Uptop-Secret: your-secret" \
-H "X-Upkeep-Secret: your-secret" \
-H "Content-Type: application/json" \
-d @kuma-backup.json
```
+1 -1
View File
@@ -81,5 +81,5 @@ Set via `UPTOP_AGG_STRATEGY` on the leader.
## Security
- Set `UPTOP_CLUSTER_SECRET` on all nodes. Without it, cluster API endpoints are unauthenticated.
- Secrets are sent in HTTP headers (`X-Uptop-Secret`). Use TLS or a reverse proxy for production.
- Secrets are sent in HTTP headers (`X-Upkeep-Secret`). Use TLS or a reverse proxy for production.
- uptop warns on startup if the cluster secret is missing or if cluster mode is active without TLS.
+1 -1
View File
@@ -52,7 +52,7 @@ func runFollowerLoop(ctx context.Context, cfg Config, eng *monitor.Engine) {
req, _ := http.NewRequest("GET", cfg.PeerURL+"/api/health", nil)
if cfg.SharedKey != "" {
req.Header.Set("X-Uptop-Secret", cfg.SharedKey)
req.Header.Set("X-Upkeep-Secret", cfg.SharedKey)
}
resp, err := client.Do(req)
+1 -1
View File
@@ -113,7 +113,7 @@ func TestFollowerLoop_SendsSecret(t *testing.T) {
var receivedSecret string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mu.Lock()
receivedSecret = r.Header.Get("X-Uptop-Secret")
receivedSecret = r.Header.Get("X-Upkeep-Secret")
mu.Unlock()
w.WriteHeader(200)
w.Write([]byte("OK"))
+3 -3
View File
@@ -90,7 +90,7 @@ func probeRegister(ctx context.Context, client *http.Client, cfg ProbeConfig) er
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Uptop-Secret", cfg.SharedKey)
req.Header.Set("X-Upkeep-Secret", cfg.SharedKey)
resp, err := client.Do(req)
if err != nil {
return err
@@ -108,7 +108,7 @@ func probeFetchAssignments(ctx context.Context, client *http.Client, cfg ProbeCo
if err != nil {
return nil, err
}
req.Header.Set("X-Uptop-Secret", cfg.SharedKey)
req.Header.Set("X-Upkeep-Secret", cfg.SharedKey)
resp, err := client.Do(req)
if err != nil {
return nil, err
@@ -180,7 +180,7 @@ func probeReportResults(ctx context.Context, client *http.Client, cfg ProbeConfi
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Uptop-Secret", cfg.SharedKey)
req.Header.Set("X-Upkeep-Secret", cfg.SharedKey)
resp, err := client.Do(req)
if err != nil {
return err
+2 -2
View File
@@ -127,7 +127,7 @@ func (s *Server) routes() http.Handler {
}
func (s *Server) requireAuth(r *http.Request) bool {
return s.cfg.ClusterKey != "" && checkSecret(r.Header.Get("X-Uptop-Secret"), s.cfg.ClusterKey)
return s.cfg.ClusterKey != "" && checkSecret(r.Header.Get("X-Upkeep-Secret"), s.cfg.ClusterKey)
}
func (s *Server) handlePush(w http.ResponseWriter, r *http.Request) {
@@ -159,7 +159,7 @@ func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
if s.cfg.ClusterKey != "" && !checkSecret(r.Header.Get("X-Uptop-Secret"), s.cfg.ClusterKey) {
if s.cfg.ClusterKey != "" && !checkSecret(r.Header.Get("X-Upkeep-Secret"), s.cfg.ClusterKey) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
+1 -1
View File
@@ -141,7 +141,7 @@ func authReq(method, url, secret string, body []byte) (*http.Response, error) {
return nil, err
}
if secret != "" {
req.Header.Set("X-Uptop-Secret", secret)
req.Header.Set("X-Upkeep-Secret", secret)
}
return http.DefaultClient.Do(req)
}