# cli: apply --wait-timeout before the first clone-probe token mint

`2ebd0d8`→[main](/content/gh/entireio/cli/commits/main/index.html)·

toothbrush·1mo ago·2 files·+36 added/-7 removed

waitForMirrorClone minted its first repo-scoped token before arming the
wait deadline. That was tolerable when the mint was a single STS
exchange, but it now spans cluster discovery and a possible login
refresh — a slow auth path could block well past a small
--wait-timeout before the timer even started. Arm the deadline first
so the authorization phase consumes the user's wait budget.

The mint goes through a package seam so the regression test can pin a
hung auth path against a 50ms timeout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

## Sessions

be119056ae83View transcript

[?\
Auth Refactor: Eliminate Static FallbacksClaude Code·Fable 5.{1m}·2 steps](/content/gh/entireio/cli/session/e6146684-ebfa-4f57-beff-34194cac8c2a#timeline-be119056ae83/index.html)

## Changes

2

- cmd/entire/cli
  
  
  - Mrepo_mirror_probe.go+15/-7
  
  - Mrepo_mirror_test.go+21

```
161 unmodified lines

162
	163
	164
	165
	166
	167
	168
	169
	170
	171
	6 unmodified lines

178
	179
	180
	181
	182
	183
	184
	185
	186
	187
	188
	189
	190
	191
	192
	193
	194
	195
	182
	196
	197
	198
	199
	1 unmodified line

201
	202
	203
	190
	191
	192
	193
	194
	195
	204
	205
	206

161 unmodified lines

return true, NewSilentError(fmt.Errorf("mirror %s is suspended", mirrorID))

// mintRepoToken mints the repo-scoped pull token for the clone probe.
// Package var so tests can substitute a slow or failing auth path.
var mintRepoToken = auth.RepoScopedToken

// waitForMirrorClone blocks until the mirror at /gh/<owner>/<repo> on
// clusterHost advertises a resolvable HEAD (the initial GitHub→EntireDB
// clone has landed) or the deadline expires. It probes the data plane's
// rather than minting once up front. Re-mints are floored at
// minReauthInterval so a flapping STS can't be amplified into a re-mint
// every probeInterval ticks.
//
// The deadline is applied before the first mint: minting now spans cluster
// discovery and a possible login refresh, so the authorization phase must
// consume the user's wait budget, not run before it.
func waitForMirrorClone(ctx context.Context, out io.Writer, clusterHost, owner, repo string, timeout time.Duration) error {
	if timeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, timeout)
		defer cancel()
	}

repoSlug := "/gh/" + owner + "/" + repo
	checkURL := fmt.Sprintf("https://%s%s/info/refs?service=git-upload-pack", clusterHost, repoSlug)

mintToken := func() (string, error) {
		return auth.RepoScopedToken(ctx, clusterHost, repoSlug, "pull")
		return mintRepoToken(ctx, clusterHost, repoSlug, "pull")
	}
	token, err := mintToken()
	if err != nil {
	1 unmodified line
	}
	lastMint := time.Now()

if timeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, timeout)
		defer cancel()
	}

ticker := time.NewTicker(probeInterval)
	defer ticker.Stop()
```

Mcmd/entire/cli/repo_mirror_probe.go+15/-7

```
4 unmodified lines

5
6
7
8
9
10
11
498 unmodified lines

510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532

4 unmodified lines

"context"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httptest"
498 unmodified lines

require.NoError(t, err)
	return &http.Request{URL: u}
}

// TestWaitForMirrorClone_TimeoutBoundsAuthorization pins that --wait-timeout
// covers the initial token mint, not just the probe loop: minting spans
// cluster discovery and a possible login refresh, so a hung auth path must
// be cut off by the user's wait budget.
//
// Not parallel: swaps the package-level mintRepoToken seam.
func TestWaitForMirrorClone_TimeoutBoundsAuthorization(t *testing.T) {
	prev := mintRepoToken
	mintRepoToken = func(ctx context.Context, _, _, _ string) (string, error) {
		<-ctx.Done() // hang until the wait deadline fires
		return "", ctx.Err()
	}
	t.Cleanup(func() { mintRepoToken = prev })

start := time.Now()
	err := waitForMirrorClone(context.Background(), io.Discard, "cluster.example", "o", "r", 50*time.Millisecond)
	require.ErrorIs(t, err, context.DeadlineExceeded)
	require.Less(t, time.Since(start), 10*time.Second, "first mint must be bounded by the wait timeout")
}
```

Mcmd/entire/cli/repo_mirror_test.go+21
