carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1//go:build unix
2
3package hostguard
4
5import (
6 "context"
7 "encoding/json"
8 "errors"
9 "os"
10 "path/filepath"
11 "sync"
12 "sync/atomic"
13 "testing"
14 "time"
15
16 "github.com/carlosframework/platform/internal/rootstate"
17)
18
19type rootTestStore struct {
20 rootstate.Store
21 dir string
22 mu sync.Mutex
23 onLock func()
24}
25
26func (s *rootTestStore) Read(n string) ([]byte, error) { return os.ReadFile(filepath.Join(s.dir, n)) }
27func (s *rootTestStore) Write(n string, b []byte) error {
28 return os.WriteFile(filepath.Join(s.dir, n), b, 0600)
29}
30func (s *rootTestStore) Names() ([]string, error) {
31 entries, e := os.ReadDir(s.dir)
32 var names []string
33 for _, n := range entries {
34 names = append(names, n.Name())
35 }
36 return names, e
37}
38func (s *rootTestStore) Lock(context.Context, string) (func(), error) {
39 s.mu.Lock()
40 if s.onLock != nil {
41 s.onLock()
42 }
43 return s.mu.Unlock, nil
44}
45func (s *rootTestStore) Close() error { return nil }
46
47func TestPlan14RootFences(t *testing.T) {
48 for _, attack := range []string{"delete", "rename", "replace lock", "forged receipt"} {
49 t.Run(attack, func(t *testing.T) {
50 f, target, captured := fenceFixture(t)
51 store := &rootTestStore{dir: t.TempDir()}
52 root := NewRootAuthority(store)
53 f.g.Root = root
54 f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "console"
55 installation := RootInstallation{Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "console", Record: "record@etag", Box: target.Box, Enrollment: target.Enrollment, Migration: 1}
56 if err := f.g.EstablishRoot(t.Context(), installation, allow); err != nil {
57 t.Fatal(err)
58 }
59 if err := f.g.With(t.Context(), target.Host, captured, allow, func() error { return nil }); err != nil {
60 t.Fatal(err)
61 }
62 if _, err := f.g.BeginFence(t.Context(), target); err != nil {
63 t.Fatal(err)
64 }
65 before, err := store.Read(target.Host + ".json")
66 if err != nil {
67 t.Fatal(err)
68 }
69 path := filepath.Join(f.g.DataDir, "hostguard", target.Host+".json")
70 switch attack {
71 case "delete":
72 if err := os.Remove(path); err != nil {
73 t.Fatal(err)
74 }
75 case "rename":
76 if err := os.Rename(filepath.Dir(path), filepath.Dir(path)+".old"); err != nil {
77 t.Fatal(err)
78 }
79 case "replace lock":
80 if err := os.Rename(f.g.LockDir, f.g.LockDir+".old"); err != nil {
81 t.Fatal(err)
82 }
83 if err := os.Mkdir(f.g.LockDir, 0700); err != nil {
84 t.Fatal(err)
85 }
86 case "forged receipt":
87 if err := os.WriteFile(path, []byte(`{"v":1,"host":"shop.example","incarnation":"inc","active_stamp":100}`), 0660); err != nil {
88 t.Fatal(err)
89 }
90 }
91 effects := 0
92 if err := f.g.With(t.Context(), target.Host, captured, allow, func() error { effects++; return nil }); !errors.Is(err, ErrFenced) || effects != 0 {
93 t.Fatalf("fence bypass: %v %d", err, effects)
94 }
95 pending, err := f.g.PendingFences(t.Context())
96 if err != nil || len(pending) != 1 || pending[0] != target {
97 t.Fatalf("lost recovery: %+v %v", pending, err)
98 }
99 stops, err := f.g.PendingStopFences(t.Context())
100 if err != nil || len(stops) != 1 || stops[0] != target {
101 t.Fatalf("ordinary stop missing from recovery: %+v %v", stops, err)
102 }
103 after, err := store.Read(target.Host + ".json")
104 if err != nil || string(before) != string(after) {
105 t.Fatal("root evidence changed", err)
106 }
107 got, err := f.g.Capture(target.Host)
108 if err != nil || got != captured {
109 t.Fatal("capture used agent state", got, err)
110 }
111 })
112 }
113}
114
115func TestPlan14RootFenceView(t *testing.T) {
116 f, target, captured := fenceFixture(t)
117 store := &rootTestStore{dir: t.TempDir()}
118 f.g.Root = NewRootAuthority(store)
119 f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "console"
120 in := RootInstallation{Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "console", Record: "record@etag", Box: target.Box, Enrollment: target.Enrollment, Migration: 1}
121 if err := f.g.EstablishRoot(t.Context(), in, allow); err != nil {
122 t.Fatal(err)
123 }
124 if _, err := f.g.BeginFence(t.Context(), target); err != nil {
125 t.Fatal(err)
126 }
127 view, err := f.g.Root.View(t.Context(), target.Host, captured)
128 if err != nil {
129 t.Fatal(err)
130 }
131 view.Pending.FenceID = "forged"
132 again, err := f.g.Root.View(t.Context(), target.Host, captured)
133 if err != nil || again.Pending.FenceID != target.FenceID {
134 t.Fatal("mutable view", err)
135 }
136 agent := f.g
137 agent.Root = nil
138 agent.FenceView = f.g.Root.View
139 effects := 0
140 if err := agent.With(t.Context(), target.Host, captured, allow, func() error { effects++; return nil }); !errors.Is(err, ErrFenced) || effects != 0 {
141 t.Fatal("agent bypassed root view", err, effects)
142 }
143 if _, err := f.g.Root.View(t.Context(), target.Host, Captured{Incarnation: "wrong", Stamp: 1}); err == nil {
144 t.Fatal("wrong tuple accepted")
145 }
146 agent.FenceView = func(context.Context, string, Captured) (FenceView, error) { return FenceView{}, nil }
147 if err := agent.With(t.Context(), target.Host, captured, allow, func() error { effects++; return nil }); err == nil || effects != 0 {
148 t.Fatal("old server granted work")
149 }
150}
151
152func TestPlan14InventoryRegistrationUnderAgentGuardDeadlocks(t *testing.T) {
153 f, target, captured := fenceFixture(t)
154 store := &rootTestStore{dir: t.TempDir()}
155 root := f.g
156 root.Root = NewRootAuthority(store)
157 f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "console"
158 in := RootInstallation{Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "console", Record: "record@etag", Box: target.Box, Enrollment: target.Enrollment, Migration: 1}
159 if err := root.EstablishRoot(t.Context(), in, allow); err != nil {
160 t.Fatal(err)
161 }
162 // Match the inventory-write call inside ReconcileAdapter.Install's work.With.
163 agent := f.g
164 agent.FenceView = root.Root.View
165 ctx, cancel := context.WithTimeout(t.Context(), 100*time.Millisecond)
166 defer cancel()
167 requested := false
168 err := agent.With(ctx, target.Host, captured, allow, func() error {
169 requested = true
170 return root.EstablishRoot(ctx, in, allow)
171 })
172 if !requested || !errors.Is(err, context.DeadlineExceeded) {
173 t.Fatalf("expected the root registration to wait for the agent's held legacy lock: %v, requested=%v", err, requested)
174 }
175}
176
177func TestPlan14FenceViewRechecksAfterUnlockedRead(t *testing.T) {
178 f, target, captured := fenceFixture(t)
179 root := f.g
180 root.Root = NewRootAuthority(&rootTestStore{dir: t.TempDir()})
181 f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "console"
182 in := RootInstallation{Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "console", Record: "record@etag", Box: target.Box, Enrollment: target.Enrollment, Migration: 1}
183 if err := root.EstablishRoot(t.Context(), in, allow); err != nil {
184 t.Fatal(err)
185 }
186 path := filepath.Join(f.g.DataDir, "hostguard", target.Host+".json")
187 before, err := os.ReadFile(path)
188 if err != nil {
189 t.Fatal(err)
190 }
191 claimBefore, err := json.Marshal(f.obs)
192 if err != nil {
193 t.Fatal(err)
194 }
195 agent := f.g
196 fencedBeforeAdmission := false
197 agent.FenceView = func(ctx context.Context, host string, c Captured) (FenceView, error) {
198 view, err := root.Root.View(ctx, host, c)
199 if err != nil {
200 return FenceView{}, err
201 }
202 // Root attempts its fence after the snapshot, before admission. With
203 // the read inside the legacy lock, it cannot commit until admission
204 // finishes. Moving only the read outside permits this same-tuple race.
205 wait, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
206 defer cancel()
207 _, err = root.BeginFence(wait, target)
208 if err == nil {
209 fencedBeforeAdmission = true
210 } else if !errors.Is(err, context.DeadlineExceeded) {
211 return FenceView{}, err
212 }
213 return view, nil
214 }
215 effects := 0
216 err = agent.With(t.Context(), target.Host, captured, allow, func() error { effects++; return nil })
217 if fencedBeforeAdmission {
218 if !errors.Is(err, ErrFenced) || effects != 0 {
219 t.Errorf("stale root snapshot admitted an already fenced session: err=%v effects=%d", err, effects)
220 }
221 } else {
222 if (!errors.Is(err, ErrFenced) || effects != 0) && (err != nil || effects != 1) {
223 t.Fatalf("unfenced admission: err=%v effects=%d", err, effects)
224 }
225 if _, err := root.BeginFence(t.Context(), target); err != nil {
226 t.Fatal(err)
227 }
228 }
229 view, err := root.Root.View(t.Context(), target.Host, captured)
230 if errors.Is(err, ErrFenced) {
231 r, e := root.Root.read(target.Host)
232 if e != nil || !r.quarantined || r.installation != in {
233 t.Fatalf("quarantine changed installation: %+v %v", r, e)
234 }
235 return
236 }
237 if err != nil || view.Pending == nil || *view.Pending != target || view.Installation != in {
238 t.Fatalf("fence changed installation identity: %+v %v", view, err)
239 }
240 after, err := os.ReadFile(path)
241 if err != nil || string(before) != string(after) {
242 t.Fatalf("fence changed agent installation record: %v", err)
243 }
244 claimAfter, err := json.Marshal(f.obs)
245 if err != nil || string(claimBefore) != string(claimAfter) {
246 t.Fatalf("fence changed claim observation: %v", err)
247 }
248 if err := root.With(t.Context(), target.Host, captured, allow, func() error { t.Fatal("root admitted fenced work"); return nil }); !errors.Is(err, ErrFenced) {
249 t.Fatalf("root fence check: %v", err)
250 }
251}
252
253func TestPlan14FenceViewWhileRootWaitsForAgent(t *testing.T) {
254 f, target, captured := fenceFixture(t)
255 root := f.g
256 store := &rootTestStore{dir: t.TempDir()}
257 root.Root = NewRootAuthority(store)
258 f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "console"
259 in := RootInstallation{Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "console", Record: "record@etag", Box: target.Box, Enrollment: target.Enrollment, Migration: 1}
260 if err := root.EstablishRoot(t.Context(), in, allow); err != nil {
261 t.Fatal(err)
262 }
263 ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second)
264 defer cancel()
265 unlock, err := acquire(ctx, f.g.LockDir, target.Host)
266 if err != nil {
267 t.Fatal(err)
268 }
269 defer func() {
270 if unlock != nil {
271 unlock()
272 }
273 }()
274 locked := make(chan struct{}, 1)
275 var acquisitions atomic.Int32
276 store.onLock = func() { acquisitions.Add(1); locked <- struct{}{} }
277 done := make(chan error, 1)
278 go func() { _, err := root.BeginFence(ctx, target); done <- err }()
279 select {
280 case <-locked:
281 case <-ctx.Done():
282 t.Fatal(ctx.Err())
283 }
284 viewDone := make(chan error, 1)
285 go func() { _, err := root.Root.View(ctx, target.Host, captured); viewDone <- err }()
286 select {
287 case err := <-viewDone:
288 if err != nil {
289 t.Fatal("honest contention fenced the view", err)
290 }
291 case <-ctx.Done():
292 t.Fatal("fence view waited on root holding a lock needed by the agent")
293 }
294 if n := acquisitions.Load(); n != 2 {
295 t.Fatalf("root mutation and view must each acquire the root lock: %d acquisitions", n)
296 }
297 unlock()
298 unlock = nil
299 select {
300 case err := <-done:
301 if !errors.Is(err, context.DeadlineExceeded) {
302 t.Fatal(err)
303 }
304 case <-ctx.Done():
305 t.Fatal("root update did not finish after agent unlocked")
306 }
307}
308
309func TestPlan14RootInventoryCommitRetainsDenials(t *testing.T) {
310 f, target, captured := fenceFixture(t)
311 f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "console"
312 store := &rootTestStore{dir: t.TempDir()}
313 f.g.Root = NewRootAuthority(store)
314 in := RootInstallation{Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "console", Record: "record", Box: target.Box, Enrollment: target.Enrollment, Migration: 1, InventoryRevision: 1}
315 effects := 0
316 commit := func() error { effects++; return nil }
317 if err := f.g.CommitRootInstallation(t.Context(), in, allow, commit); err != nil || effects != 1 {
318 t.Fatalf("initial root inventory: %v %d", err, effects)
319 }
320 if _, err := f.g.BeginFence(t.Context(), target); err != nil {
321 t.Fatal(err)
322 }
323 in.InventoryRevision++
324 if err := f.g.CommitRootInstallation(t.Context(), in, allow, commit); !errors.Is(err, ErrFenced) || effects != 1 {
325 t.Fatalf("fenced inventory commit: %v %d", err, effects)
326 }
327 view, err := f.g.Root.View(t.Context(), target.Host, captured)
328 if err != nil || view.Installation.InventoryRevision != 1 || view.Pending == nil || *view.Pending != target {
329 t.Fatalf("inventory erased fence: %+v %v", view, err)
330 }
331}
332
333func TestPlan14ColdHandoverRefreshesRootInventory(t *testing.T) { testRootColdHandover(t, false) }
334func TestPlan14MigrationPredecessorReadmission(t *testing.T) { testRootColdHandover(t, true) }
335func testRootColdHandover(t *testing.T, migration bool) {
336 f, prior, captured := fenceFixture(t)
337 f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "app"
338 store := &rootTestStore{dir: t.TempDir()}
339 f.g.Root = NewRootAuthorityWithInventory(store, func(_ context.Context, host string, c Captured) (uint64, error) {
340 if host != prior.Host || c.Stamp != 17 {
341 return 0, ErrStampMismatch
342 }
343 return 2, nil
344 })
345 in := RootInstallation{Host: prior.Host, Incarnation: prior.Incarnation, Stamp: prior.Stamp, Account: "bab", App: "app", Record: "record", Box: prior.Box, Enrollment: prior.Enrollment, Migration: 1, InventoryRevision: 5}
346 if err := f.g.EstablishRoot(t.Context(), in, allow); err != nil {
347 t.Fatal(err)
348 }
349 if migration {
350 if err := f.g.Root.RetainMigrationDeny(t.Context(), in, prior.Session); err != nil {
351 t.Fatal(err)
352 }
353 }
354 if _, err := f.g.BeginFence(t.Context(), prior); err != nil {
355 t.Fatal(err)
356 }
357 at := time.Now().UTC()
358 if err := f.g.CompleteFence(t.Context(), prior, []byte("verified stop"), at, func(context.Context, FenceTarget, []byte, time.Time) error { return nil }); err != nil {
359 t.Fatal(err)
360 }
361 next := prior
362 next.FenceID = "cold-next"
363 next.Stamp = 17
364 next.Session = 17
365 f.obs.Claim.Holder.Stamp, f.obs.Claim.Holder.Session = 17, 17
366 if err := f.g.BeginColdHandover(t.Context(), prior, next, allow); err != nil {
367 t.Fatal(err)
368 }
369 if err := f.g.Activate(t.Context(), prior.Host, prior.Box, nil); err == nil {
370 t.Fatal("activation cleared cold barrier")
371 }
372 if migration {
373 fail := errors.New("interrupted handover")
374 if err := f.g.CompleteColdHandover(t.Context(), next, false, allow, func() error { return fail }); !errors.Is(err, fail) {
375 t.Fatal(err)
376 }
377 if err := f.g.With(t.Context(), prior.Host, Captured{Incarnation: prior.Incarnation, Stamp: 17}, allow, func() error { t.Fatal("pending successor ran"); return nil }); err == nil {
378 t.Fatal("pending handover allowed")
379 }
380 }
381 completed := 0
382 if err := f.g.CompleteColdHandover(t.Context(), next, false, allow, func() error { completed++; return nil }); err != nil {
383 t.Fatal(err)
384 }
385 if err := f.g.CompleteColdHandover(t.Context(), next, true, allow, func() error { completed++; return nil }); err != nil || completed != 1 {
386 t.Fatal("handover replay", err, completed)
387 }
388 view, err := f.g.Root.View(t.Context(), prior.Host, Captured{Incarnation: prior.Incarnation, Stamp: 17})
389 if err != nil || view.Installation.InventoryRevision != 2 || view.Barrier != nil {
390 t.Fatalf("successor snapshot: %+v %v", view, err)
391 }
392 effects := 0
393 if err := f.g.With(t.Context(), prior.Host, view.Captured, allow, func() error { effects++; return nil }); err != nil || effects != 1 {
394 t.Fatalf("successor refused: %v", err)
395 }
396 if err := f.g.With(t.Context(), prior.Host, captured, allow, func() error { effects++; return nil }); err == nil || effects != 1 {
397 t.Fatal("predecessor replay admitted")
398 }
399 receipt, err := f.g.FenceReceiptFor(t.Context(), prior)
400 if err != nil || !receipt.EvidenceAt.Equal(at) {
401 t.Fatalf("old stop evidence lost: %+v %v", receipt, err)
402 }
403}
404
405func TestPlan14InstallationCommitRecovery(t *testing.T) {
406 f, target, captured := fenceFixture(t)
407 store := &rootTestStore{dir: t.TempDir()}
408 f.g.Root = NewRootAuthority(store)
409 f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "console"
410 in := RootInstallation{Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "console", Record: "record", Box: target.Box, Enrollment: target.Enrollment, Migration: 1, InventoryRevision: 1}
411 crash := errors.New("simulated crash after inventory commit")
412 intent := []byte("typed inventory request")
413 err := f.g.CommitRootInventory(t.Context(), in, intent, allow, nil, func(RootInstallation) error { return crash })
414 if !errors.Is(err, crash) {
415 t.Fatal(err)
416 }
417 f.g.Root = NewRootAuthority(store)
418 if err := f.g.With(t.Context(), target.Host, captured, allow, func() error { t.Fatal("pending installation admitted"); return nil }); err == nil {
419 t.Fatal("admitted")
420 }
421 pending, err := f.g.Root.PendingInstallations()
422 if err != nil || len(pending) != 1 || string(pending[0]) != string(intent) {
423 t.Fatal(pending, err)
424 }
425 if _, err = f.g.Root.View(t.Context(), target.Host, captured); err == nil {
426 t.Fatal("pending view available")
427 }
428 if err = f.g.CommitRootInventory(t.Context(), in, []byte("other"), allow, nil, func(RootInstallation) error { t.Fatal("changed intent committed"); return nil }); err == nil {
429 t.Fatal("changed intent accepted")
430 }
431 if err = f.g.CommitRootInventory(t.Context(), in, intent, allow, nil, func(RootInstallation) error { return nil }); err != nil {
432 t.Fatal(err)
433 }
434 if err = f.g.With(t.Context(), target.Host, captured, allow, func() error { return nil }); err != nil {
435 t.Fatal(err)
436 }
437 pending, err = f.g.Root.PendingInstallations()
438 if err != nil || len(pending) != 0 {
439 t.Fatal(pending, err)
440 }
441}
442
443func TestPlan14RootNoInstallationNeedsExplicitEmptyState(t *testing.T) {
444 f, target, captured := fenceFixture(t)
445 f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "app"
446 store := &rootTestStore{dir: t.TempDir()}
447 f.g.Root = NewRootAuthority(store)
448 effects := 0
449 effect := func() error { effects++; return nil }
450 if err := f.g.WithNoInstallation(t.Context(), target.Host, captured.Incarnation, allow, effect); err == nil || effects != 0 {
451 t.Fatal("missing root record authorized absence", err)
452 }
453 in := RootInstallation{Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "app", Record: "record", Box: target.Box, Enrollment: target.Enrollment, Migration: 1, Empty: true}
454 if err := f.g.EstablishRoot(t.Context(), in, allow); err != nil {
455 t.Fatal(err)
456 }
457 if err := f.g.WithNoInstallation(t.Context(), target.Host, captured.Incarnation, allow, effect); err != nil || effects != 1 {
458 t.Fatal("explicit empty refused", err)
459 }
460 if _, err := f.g.BeginFence(t.Context(), target); err != nil {
461 t.Fatal(err)
462 }
463 if err := f.g.WithNoInstallation(t.Context(), target.Host, captured.Incarnation, allow, effect); err == nil || effects != 1 {
464 t.Fatal("empty bypassed fence", err)
465 }
466}
467
468func TestPlan14AdmissionFenceUsesRootView(t *testing.T) {
469 f, target, captured := fenceFixture(t)
470 store := &rootTestStore{dir: t.TempDir()}
471 root := f.g
472 root.Root = NewRootAuthority(store)
473 f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "console"
474 in := RootInstallation{Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "console", Record: "record", Box: target.Box, Enrollment: target.Enrollment, Migration: 1}
475 if err := root.EstablishRoot(t.Context(), in, allow); err != nil {
476 t.Fatal(err)
477 }
478 agent := f.g
479 agent.FenceView = root.Root.View
480 for _, g := range []Guard{root, agent} {
481 if closed, err := g.AdmissionFenced(target.Host, f.obs.Claim, captured); err != nil || closed {
482 t.Fatal(closed, err)
483 }
484 }
485 if _, err := root.BeginFence(t.Context(), target); err != nil {
486 t.Fatal(err)
487 }
488 if err := os.Remove(filepath.Join(agent.DataDir, "hostguard", target.Host+".json")); err != nil {
489 t.Fatal(err)
490 }
491 for _, g := range []Guard{root, agent} {
492 ctx, cancel := context.WithTimeout(t.Context(), time.Second)
493 err := g.WithLocal(ctx, target.Host, func() error {
494 closed, err := g.AdmissionFenced(target.Host, f.obs.Claim, captured)
495 if err == nil && !closed {
496 return errors.New("root fence was ignored")
497 }
498 return err
499 })
500 cancel()
501 if err != nil {
502 t.Fatal(err)
503 }
504 }
505 agent.FenceView = func(context.Context, string, Captured) (FenceView, error) { return FenceView{}, nil }
506 if _, err := agent.AdmissionFenced(target.Host, f.obs.Claim, captured); err == nil {
507 t.Fatal("malformed view granted admission")
508 }
509 v, err := root.Root.View(t.Context(), target.Host, captured)
510 if err != nil {
511 t.Fatal(err)
512 }
513 v.Pending = nil
514 v.Retired = []Captured{captured}
515 if !errors.Is(v.admit(target.Host, captured, f.obs.Claim.Holder.Session), ErrRetiredStamp) {
516 t.Fatal("retired view granted admission")
517 }
518}
519