fix: propagate first_seen_at into lease and hop route signing (extreme minimal)

Lee Yunjin committed Apr 30, 2026 at 11:16 UTC be6d4a3dc829c9a2933a5850d66fa69c79d70f76
3 files changed +5
portal/lease.go
+1
@@ -443,6 +443,7 @@ func (r *leaseRegistry) RegisterHopRoute(route *types.HopRoute, now time.Time) (
443 },
444 Hostname: matchHostname,
445 Metadata: route.Metadata.Copy(),
446 + FirstSeenAt: route.FirstSeenAt.UTC(),
447 ExpiresAt: expiresAt,
448 hopToken: matchToken,
449 hopNextOverlayIPv4: overlayIPv4,
sdk/api_client.go
+1
@@ -233,6 +233,7 @@ func (l *listener) syncHopRoutes(ctx context.Context, method string, expiresAt t
233
234 var syncErr error
235 for _, unsignedRoute := range orderedRoutes {
236 + unsignedRoute.FirstSeenAt = expiresAt.Add(-30 * time.Second)
237 route, err := auth.SignHopRoute(method, unsignedRoute, l.identity, expiresAt)
238 if err != nil {
239 if method == http.MethodDelete {
types/api.go
+3
@@ -130,6 +130,7 @@ type HopRoute struct {
130 Metadata LeaseMetadata `json:"metadata,omitempty"`
131 ForwardRelay RelayDescriptor `json:"forward_relay"`
132 ForwardToken string `json:"forward_token"`
133 + FirstSeenAt time.Time `json:"first_seen_at,omitempty"`
134 ExpiresAt time.Time `json:"expires_at,omitempty"`
135 Signature string `json:"signature,omitempty"`
136 }
@@ -148,6 +149,7 @@ func HopRouteBytes(method string, route HopRoute) ([]byte, error) {
149 MatchToken string `json:"match_token"`
150 ForwardRelay json.RawMessage `json:"forward_relay"`
151 ForwardToken string `json:"forward_token"`
152 + FirstSeenAtUnixNano int64 `json:"first_seen_at_unix_nano"`
153 ExpiresAtUnixNano int64 `json:"expires_at_unix_nano"`
154 }{
155 Purpose: "portal hop route v1",
@@ -158,6 +160,7 @@ func HopRouteBytes(method string, route HopRoute) ([]byte, error) {
160 MatchToken: strings.TrimSpace(route.MatchToken),
161 ForwardRelay: json.RawMessage(forwardRelay),
162 ForwardToken: strings.TrimSpace(route.ForwardToken),
163 + FirstSeenAtUnixNano: route.FirstSeenAt.UTC().UnixNano(),
164 ExpiresAtUnixNano: route.ExpiresAt.UTC().UnixNano(),
165 }
166 return json.Marshal(payload)