diff --git a/server/cmd/api/api/api.go b/server/cmd/api/api/api.go index d7a528d2f..e0dc3a677 100644 --- a/server/cmd/api/api/api.go +++ b/server/cmd/api/api/api.go @@ -46,7 +46,7 @@ var _ OTLPExporter = (*events.OTLPExportController)(nil) // S2Storage controls the optional S2 storage sink, which no-ops when the VM // has no S2 credentials. Implemented by *events.S2StorageController. type S2Storage interface { - Start(ctx context.Context) error + Start(ctx context.Context, afterSeq uint64) error Stop(ctx context.Context) error Running() bool EverStarted() bool @@ -133,7 +133,13 @@ type ApiService struct { // exportMu serializes OTLP export reconciliation independently of monitorMu, // so a toggle-off drain (bounded by otlpStopTimeout) never blocks concurrent // telemetry reads/writes on monitorMu. - exportMu sync.Mutex + exportMu sync.Mutex + // storageMu serializes S2 storage reconciliation against telemetry commits. + // Unlike exportMu it is also taken under monitorMu: PUT and PATCH hold it + // from the storage-off check until the config is committed or rolled back, + // so reconcileStorage only reads a settled config. reconcileStorage never + // takes monitorMu, so the order is always monitorMu then storageMu. + storageMu sync.Mutex lifecycleCtx context.Context lifecycleCancel context.CancelFunc } diff --git a/server/cmd/api/api/telemetry.go b/server/cmd/api/api/telemetry.go index c752fd19f..d45bd0cd7 100644 --- a/server/cmd/api/api/telemetry.go +++ b/server/cmd/api/api/telemetry.go @@ -16,6 +16,10 @@ import ( // otlpStopTimeout bounds how long a runtime export toggle-off waits to drain. const otlpStopTimeout = 5 * time.Second +// storageDisableConflict is the 409 for turning storage off after the sink has +// opened on this instance. +const storageDisableConflict = "storage cannot be disabled on an instance that has already started storage" + // GetTelemetry handles GET /telemetry. // Returns the current telemetry configuration. Returns 404 if telemetry is not configured. func (s *ApiService) GetTelemetry(_ context.Context, _ oapi.GetTelemetryRequestObject) (oapi.GetTelemetryResponseObject, error) { @@ -32,10 +36,11 @@ func (s *ApiService) GetTelemetry(_ context.Context, _ oapi.GetTelemetryRequestO // Sets the telemetry configuration. Returns 201 if not previously configured, 200 if it was. // Setting every configurable category to enabled:false clears the configuration (200). func (s *ApiService) PutTelemetry(ctx context.Context, req oapi.PutTelemetryRequestObject) (oapi.PutTelemetryResponseObject, error) { - // Reconcile export after monitorMu is released (defers run LIFO), so the - // toggle-off drain does not hold the API-wide lock. It reads the committed - // desired state from the session, so it is correct regardless of how this - // request exits. + // Reconcile the sinks after monitorMu is released (defers run LIFO), so the + // export toggle-off drain does not hold the API-wide lock. Both read the + // committed desired state from the session, so they are correct regardless + // of how this request exits. + defer s.reconcileStorage(ctx) defer s.reconcileExport(ctx) s.monitorMu.Lock() defer s.monitorMu.Unlock() @@ -45,6 +50,21 @@ func (s *ApiService) PutTelemetry(ctx context.Context, req oapi.PutTelemetryRequ return oapi.PutTelemetry400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: err.Error()}}, nil } + // storageMu is held until the config is settled, committed or rolled back, + // so reconcileStorage never reads a provisional one: a storage-on update + // whose capture fails to apply would otherwise leave the sink open under the + // storage-off config it rolls back to. It also keeps a start in flight from + // racing the storage-off check below. + s.storageMu.Lock() + defer s.storageMu.Unlock() + + // Storage cannot be turned off once anything may have been persisted, a + // clear included. Checked before anything is committed, so a 409 leaves the + // session exactly as it was. + if !cfg.StoreS2 && s.storageEverStarted() { + return oapi.PutTelemetry409JSONResponse{ConflictErrorJSONResponse: oapi.ConflictErrorJSONResponse{Message: storageDisableConflict}}, nil + } + wasActive := s.telemetrySession.Active() if allDisabled { @@ -52,7 +72,7 @@ func (s *ApiService) PutTelemetry(ctx context.Context, req oapi.PutTelemetryRequ s.telemetrySession.Stop() s.stopTelemetryState() } - return oapi.PutTelemetry200JSONResponse(s.stoppedTelemetryResponse()), nil + return oapi.PutTelemetry200JSONResponse(s.stoppedTelemetryResponse(cfg.StoreS2)), nil } // Commit the config first so the filter is live before the collector emits, @@ -82,7 +102,8 @@ func (s *ApiService) PutTelemetry(ctx context.Context, req oapi.PutTelemetryRequ // Partially updates the telemetry configuration. Returns 404 if not configured. // Setting every configurable category to enabled:false clears the configuration (200). func (s *ApiService) PatchTelemetry(ctx context.Context, req oapi.PatchTelemetryRequestObject) (oapi.PatchTelemetryResponseObject, error) { - // See PutTelemetry: reconcile export after monitorMu is released. + // See PutTelemetry: reconcile the sinks after monitorMu is released. + defer s.reconcileStorage(ctx) defer s.reconcileExport(ctx) s.monitorMu.Lock() defer s.monitorMu.Unlock() @@ -91,18 +112,26 @@ func (s *ApiService) PatchTelemetry(ctx context.Context, req oapi.PatchTelemetry return oapi.PatchTelemetry404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "telemetry is not configured"}}, nil } - // Nothing to merge when neither the category block nor the export toggle is + // Nothing to merge when neither the category block nor a sink toggle is // present; skip the reconcile and echo current state, as before. - if req.Body == nil || (req.Body.Browser == nil && req.Body.Export == nil) { + if req.Body == nil || (req.Body.Browser == nil && req.Body.Export == nil && req.Body.Storage == nil) { return oapi.PatchTelemetry200JSONResponse(s.buildTelemetryResponse()), nil } + // See PutTelemetry: storageMu is held until the config is settled, and the + // storage-off check runs before anything is committed. + s.storageMu.Lock() + defer s.storageMu.Unlock() + prev := s.telemetrySession.Config() cfg, allDisabled := mergeTelemetryConfig(prev, req.Body) + if !cfg.StoreS2 && s.storageEverStarted() { + return oapi.PatchTelemetry409JSONResponse{ConflictErrorJSONResponse: oapi.ConflictErrorJSONResponse{Message: storageDisableConflict}}, nil + } if allDisabled { s.telemetrySession.Stop() s.stopTelemetryState() - return oapi.PatchTelemetry200JSONResponse(s.stoppedTelemetryResponse()), nil + return oapi.PatchTelemetry200JSONResponse(s.stoppedTelemetryResponse(cfg.StoreS2)), nil } // Commit first so the filter is live before the collector emits, then @@ -178,8 +207,44 @@ func (s *ApiService) reconcileExport(ctx context.Context) { } } +// reconcileStorage opens the S2 storage sink once the committed telemetry +// config calls for it: a capture session is active and storage is on. The +// sink stores only events captured under that config, not what the ring still +// holds from earlier storage-off capture. It never closes the sink: the writer +// is single-use and binds its stream for the life of the instance, so it stops +// only at shutdown, and the storage-off guard in PUT and PATCH keeps a +// storage-off config from coexisting with an open sink. Like reconcileExport +// it reads the desired state from the session and runs after monitorMu is +// released; storageMu makes that state settled, since PUT and PATCH hold it +// until they commit or roll back. It is best-effort: a failed start is logged, +// never surfaced, and retried by the next request. No-op when the VM has no +// storage controller. +func (s *ApiService) reconcileStorage(ctx context.Context) { + if s.s2Storage == nil { + return + } + s.storageMu.Lock() + defer s.storageMu.Unlock() + + afterSeq, store := s.telemetrySession.StoreS2After() + if !store || s.s2Storage.EverStarted() { + return + } + // Root the sink on the app lifecycle, not this request; only its logs + // carry the request context. + if err := s.s2Storage.Start(s.lifecycleCtx, afterSeq); err != nil { + logger.FromContext(ctx).Error("s2 storage failed to start", "err", err) + } +} + +// storageEverStarted reports whether the S2 sink has opened on this instance, +// which is false when the VM has no storage controller. +func (s *ApiService) storageEverStarted() bool { + return s.s2Storage != nil && s.s2Storage.EverStarted() +} + // stopTelemetryState tears down optional capture and middleware after a session is -// cleared. Export is reconciled separately, after monitorMu is released. +// cleared. The sinks are reconciled separately, after monitorMu is released. func (s *ApiService) stopTelemetryState() { if err := s.cdpMonitor.SetTelemetry(false); err != nil { logger.FromContext(s.lifecycleCtx).Warn("failed to clean up telemetry capture", "err", err) @@ -200,11 +265,12 @@ func (s *ApiService) buildTelemetryResponse() oapi.TelemetryState { return resp } -// stoppedTelemetryResponse reports the cleared configuration. Seq and the -// dropped count are process-scoped, so they survive a session ending. -func (s *ApiService) stoppedTelemetryResponse() oapi.TelemetryState { +// stoppedTelemetryResponse reports the cleared configuration, echoing the +// storage toggle the clearing request carried. Seq and the dropped count are +// process-scoped, so they survive a session ending. +func (s *ApiService) stoppedTelemetryResponse(storeS2 bool) oapi.TelemetryState { return oapi.TelemetryState{ - Config: disabledConfig(), + Config: disabledConfig(storeS2), Seq: int64(s.telemetrySession.Seq()), DroppedEvents: lo.ToPtr(int64(s.telemetrySession.DroppedEvents())), } @@ -276,11 +342,12 @@ func containsCategory(cats []oapi.TelemetryEventCategory, target oapi.TelemetryE // config, whether the result is empty (stop signal), and any error. func telemetryConfigFromOAPI(cfg *oapi.BrowserTelemetryConfig) (telemetry.TelemetryConfig, bool, error) { exportOTLP := exportOTLPFromOAPI(cfg) + storeS2 := storeS2FromOAPI(cfg) if cfg == nil || cfg.Browser == nil { // No per-category settings: resolve to the explicit default set so the // effective categories are known before the collector is reconciled. cats := append([]oapi.TelemetryEventCategory(nil), events.DefaultCategories...) - return telemetry.TelemetryConfig{Categories: cats, ExportOTLP: exportOTLP}, false, nil + return telemetry.TelemetryConfig{Categories: cats, ExportOTLP: exportOTLP, StoreS2: storeS2}, false, nil } cats := make([]oapi.TelemetryEventCategory, 0, len(events.UserCategories)) @@ -290,11 +357,14 @@ func telemetryConfigFromOAPI(cfg *oapi.BrowserTelemetryConfig) (telemetry.Teleme } } if len(cats) == 0 { - return telemetry.TelemetryConfig{}, true, nil + // The stop signal keeps the storage toggle: a clear is still checked + // against storage that has already started, and echoes what it asked for. + return telemetry.TelemetryConfig{StoreS2: storeS2}, true, nil } return telemetry.TelemetryConfig{ Categories: cats, ExportOTLP: exportOTLP, + StoreS2: storeS2, ExcludedCdpMethods: excludedCdpMethodsFromOAPI(cfg), }, false, nil } @@ -308,6 +378,16 @@ func exportOTLPFromOAPI(cfg *oapi.BrowserTelemetryConfig) bool { return false } +// storeS2FromOAPI reads the storage toggle from a config, defaulting to true +// (on) when the storage block is omitted: the opposite of export, so callers +// that predate the field keep storing. +func storeS2FromOAPI(cfg *oapi.BrowserTelemetryConfig) bool { + if cfg != nil && cfg.Storage != nil && cfg.Storage.Enabled != nil { + return *cfg.Storage.Enabled + } + return true +} + // mergeTelemetryConfig applies patch overrides onto current, returning the merged config and // whether every configurable category ended up disabled (stop signal). Only categories with an // explicit Enabled field in patch are changed; omitted categories keep their current state. @@ -333,11 +413,16 @@ func mergeTelemetryConfig(current telemetry.TelemetryConfig, patch *oapi.Browser } } - // Export follows the same patch semantics: an omitted toggle is unchanged. + // Export and storage follow the same patch semantics: an omitted toggle is + // unchanged. exportOTLP := current.ExportOTLP if patch.Export != nil && patch.Export.Otlp != nil && patch.Export.Otlp.Enabled != nil { exportOTLP = *patch.Export.Otlp.Enabled } + storeS2 := current.StoreS2 + if patch.Storage != nil && patch.Storage.Enabled != nil { + storeS2 = *patch.Storage.Enabled + } // So do the cdp_command exclusions: an omitted list is unchanged, an empty // one clears them. @@ -347,17 +432,18 @@ func mergeTelemetryConfig(current telemetry.TelemetryConfig, patch *oapi.Browser } if len(active) == 0 { - return telemetry.TelemetryConfig{}, true + return telemetry.TelemetryConfig{StoreS2: storeS2}, true } cats := make([]oapi.TelemetryEventCategory, 0, len(active)) for c := range active { cats = append(cats, c) } - return telemetry.TelemetryConfig{Categories: cats, ExportOTLP: exportOTLP, ExcludedCdpMethods: excluded}, false + return telemetry.TelemetryConfig{Categories: cats, ExportOTLP: exportOTLP, StoreS2: storeS2, ExcludedCdpMethods: excluded}, false } -// disabledConfig returns a BrowserTelemetryConfig with every configurable category explicitly disabled. -func disabledConfig() oapi.BrowserTelemetryConfig { +// disabledConfig returns a BrowserTelemetryConfig with every configurable +// category explicitly disabled and the given storage toggle. +func disabledConfig(storeS2 bool) oapi.BrowserTelemetryConfig { off := func() *oapi.BrowserTelemetryCategoryConfig { return &oapi.BrowserTelemetryCategoryConfig{Enabled: lo.ToPtr(false)} } @@ -374,7 +460,8 @@ func disabledConfig() oapi.BrowserTelemetryConfig { Screenshot: off(), Captcha: off(), }, - Export: exportConfigToOAPI(false), + Export: exportConfigToOAPI(false), + Storage: storageConfigToOAPI(storeS2), } } @@ -385,6 +472,11 @@ func exportConfigToOAPI(enabled bool) *oapi.BrowserTelemetryExportConfig { } } +// storageConfigToOAPI renders the storage toggle for API responses. +func storageConfigToOAPI(enabled bool) *oapi.BrowserTelemetryStorageConfig { + return &oapi.BrowserTelemetryStorageConfig{Enabled: lo.ToPtr(enabled)} +} + // telemetryConfigToOAPI converts a telemetry.TelemetryConfig to an oapi.BrowserTelemetryConfig // suitable for API responses. The auto-managed Monitor category is not represented. func telemetryConfigToOAPI(cfg telemetry.TelemetryConfig) oapi.BrowserTelemetryConfig { @@ -410,6 +502,7 @@ func telemetryConfigToOAPI(cfg telemetry.TelemetryConfig) oapi.BrowserTelemetryC Screenshot: enabled(events.Screenshot), Captcha: enabled(events.Captcha), }, - Export: exportConfigToOAPI(cfg.ExportOTLP), + Export: exportConfigToOAPI(cfg.ExportOTLP), + Storage: storageConfigToOAPI(cfg.StoreS2), } } diff --git a/server/cmd/api/api/telemetry_test.go b/server/cmd/api/api/telemetry_test.go index 3e11e4fd7..562377c5a 100644 --- a/server/cmd/api/api/telemetry_test.go +++ b/server/cmd/api/api/telemetry_test.go @@ -13,6 +13,7 @@ import ( oapi "github.com/kernel/kernel-images/server/lib/oapi" "github.com/kernel/kernel-images/server/lib/recorder" "github.com/kernel/kernel-images/server/lib/scaletozero" + "github.com/kernel/kernel-images/server/lib/telemetry" "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -720,3 +721,581 @@ func TestTelemetryStateStaysCompatibleWithOlderImages(t *testing.T) { assert.True(t, *cfg.Browser.Control.Enabled) assert.Nil(t, cfg.Browser.Control.Cdp) } + +func TestTelemetryStorageToggle(t *testing.T) { + ctx := context.Background() + tr, fa := true, false + storageOff := func() *oapi.BrowserTelemetryConfig { + return &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{Network: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}}, + Storage: &oapi.BrowserTelemetryStorageConfig{Enabled: &fa}, + } + } + storageOmitted := func() *oapi.BrowserTelemetryConfig { + return &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{Network: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}}, + } + } + storageOf := func(t *testing.T, cfg oapi.BrowserTelemetryConfig) bool { + t.Helper() + require.NotNil(t, cfg.Storage, "storage must always be echoed") + require.NotNil(t, cfg.Storage.Enabled) + return *cfg.Storage.Enabled + } + + t.Run("omitted storage means on", func(t *testing.T) { + cfg, allDisabled, err := telemetryConfigFromOAPI(nil) + require.NoError(t, err) + assert.False(t, allDisabled) + assert.True(t, cfg.StoreS2) + + cfg, _, err = telemetryConfigFromOAPI(storageOmitted()) + require.NoError(t, err) + assert.True(t, cfg.StoreS2) + + cfg, _, err = telemetryConfigFromOAPI(storageOff()) + require.NoError(t, err) + assert.False(t, cfg.StoreS2) + }) + + t.Run("a clear keeps the storage toggle it carried", func(t *testing.T) { + cfg, allDisabled, err := telemetryConfigFromOAPI(&oapi.BrowserTelemetryConfig{ + Browser: allCategoriesDisabled(), + Storage: &oapi.BrowserTelemetryStorageConfig{Enabled: &fa}, + }) + require.NoError(t, err) + assert.True(t, allDisabled) + assert.False(t, cfg.StoreS2) + + // The zero value is off, so the positive case is what proves the + // toggle is carried rather than dropped. + cfg, allDisabled, err = telemetryConfigFromOAPI(&oapi.BrowserTelemetryConfig{Browser: allCategoriesDisabled()}) + require.NoError(t, err) + assert.True(t, allDisabled) + assert.True(t, cfg.StoreS2) + + for _, current := range []bool{false, true} { + merged, allDisabled := mergeTelemetryConfig(telemetry.TelemetryConfig{Categories: events.DefaultCategories, StoreS2: current}, &oapi.BrowserTelemetryConfig{Browser: allCategoriesDisabled()}) + assert.True(t, allDisabled) + assert.Equal(t, current, merged.StoreS2, "an all-disabled patch must keep the current storage toggle") + } + }) + + t.Run("patch leaves an omitted toggle alone", func(t *testing.T) { + current := telemetry.TelemetryConfig{Categories: []oapi.TelemetryEventCategory{events.Network}, StoreS2: false} + merged, _ := mergeTelemetryConfig(current, &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}}, + }) + assert.False(t, merged.StoreS2) + + merged, _ = mergeTelemetryConfig(current, &oapi.BrowserTelemetryConfig{ + Storage: &oapi.BrowserTelemetryStorageConfig{Enabled: &tr}, + }) + assert.True(t, merged.StoreS2) + }) + + t.Run("GET, PUT and PATCH echo storage exactly", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: storageOff()}) + require.NoError(t, err) + r201, ok := resp.(oapi.PutTelemetry201JSONResponse) + require.True(t, ok, "expected 201, got %T", resp) + assert.False(t, storageOf(t, r201.Config)) + + got, err := svc.GetTelemetry(ctx, oapi.GetTelemetryRequestObject{}) + require.NoError(t, err) + assert.False(t, storageOf(t, got.(oapi.GetTelemetry200JSONResponse).Config)) + + // A category-only PATCH is not a storage change. + presp, err := svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}}, + }}) + require.NoError(t, err) + assert.False(t, storageOf(t, presp.(oapi.PatchTelemetry200JSONResponse).Config)) + + // A storage-only PATCH is applied, not treated as an empty body. + presp, err = svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{ + Storage: &oapi.BrowserTelemetryStorageConfig{Enabled: &tr}, + }}) + require.NoError(t, err) + assert.True(t, storageOf(t, presp.(oapi.PatchTelemetry200JSONResponse).Config)) + assert.True(t, svc.telemetrySession.Config().StoreS2) + + resp, err = svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: storageOmitted()}) + require.NoError(t, err) + assert.True(t, storageOf(t, resp.(oapi.PutTelemetry200JSONResponse).Config)) + }) + + t.Run("a clear echoes the storage toggle it carried", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: storageOff()}) + require.NoError(t, err) + + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{ + Browser: allCategoriesDisabled(), + Storage: &oapi.BrowserTelemetryStorageConfig{Enabled: &fa}, + }}) + require.NoError(t, err) + assert.False(t, storageOf(t, resp.(oapi.PutTelemetry200JSONResponse).Config)) + assert.False(t, svc.telemetrySession.Active()) + + _, err = svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: storageOff()}) + require.NoError(t, err) + presp, err := svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{Browser: allCategoriesDisabled()}}) + require.NoError(t, err) + assert.False(t, storageOf(t, presp.(oapi.PatchTelemetry200JSONResponse).Config), "a clearing PATCH keeps the session's storage toggle") + }) +} + +// stubS2Storage records starts for storage-toggle tests. Locked because +// reconcileStorage runs outside monitorMu. +type stubS2Storage struct { + mu sync.Mutex + starts int + running bool + everStarted bool + startErr error + afterSeq uint64 +} + +func (s *stubS2Storage) Start(_ context.Context, afterSeq uint64) error { + s.mu.Lock() + defer s.mu.Unlock() + if s.startErr != nil { + return s.startErr + } + s.starts++ + s.afterSeq = afterSeq + s.running, s.everStarted = true, true + return nil +} + +func (s *stubS2Storage) Stop(context.Context) error { + s.mu.Lock() + defer s.mu.Unlock() + s.running = false + return nil +} + +func (s *stubS2Storage) Running() bool { + s.mu.Lock() + defer s.mu.Unlock() + return s.running +} + +func (s *stubS2Storage) EverStarted() bool { + s.mu.Lock() + defer s.mu.Unlock() + return s.everStarted +} + +func (s *stubS2Storage) startCount() int { + s.mu.Lock() + defer s.mu.Unlock() + return s.starts +} + +func TestTelemetryStorageStart(t *testing.T) { + ctx := context.Background() + tr, fa := true, false + networkOn := func(storage *bool) *oapi.BrowserTelemetryConfig { + cfg := &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{Network: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}}, + } + if storage != nil { + cfg.Storage = &oapi.BrowserTelemetryStorageConfig{Enabled: storage} + } + return cfg + } + cleared := func(storage *bool) *oapi.BrowserTelemetryConfig { + cfg := &oapi.BrowserTelemetryConfig{Browser: allCategoriesDisabled()} + if storage != nil { + cfg.Storage = &oapi.BrowserTelemetryStorageConfig{Enabled: storage} + } + return cfg + } + newStorageService := func(t *testing.T) (*ApiService, *stubS2Storage) { + svc := newTestService(t, newMockRecordManager()) + st := &stubS2Storage{} + svc.s2Storage = st + return svc, st + } + publishNetwork := func(t *testing.T, svc *ApiService) { + t.Helper() + _, ok := svc.telemetrySession.Publish(events.Event{Type: "network.request", Category: events.Network, Source: oapi.BrowserEventSource{Kind: oapi.Cdp}}) + require.True(t, ok, "the session must admit the event") + } + + t.Run("storage off never opens the sink", func(t *testing.T) { + svc, st := newStorageService(t) + + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(&fa)}) + require.NoError(t, err) + r201, ok := resp.(oapi.PutTelemetry201JSONResponse) + require.True(t, ok, "expected 201, got %T", resp) + assert.False(t, *r201.Config.Storage.Enabled) + require.True(t, svc.telemetrySession.Active(), "the session runs; only storage is off") + + // Events flow to the ring (and so to the live stream and export) but + // nothing is opened that could persist them. + publishNetwork(t, svc) + assert.False(t, st.EverStarted()) + assert.Equal(t, 0, st.startCount()) + }) + + t.Run("storage omitted opens the sink once", func(t *testing.T) { + svc, st := newStorageService(t) + + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(nil)}) + require.NoError(t, err) + require.IsType(t, oapi.PutTelemetry201JSONResponse{}, resp) + assert.Equal(t, 1, st.startCount()) + assert.True(t, st.Running()) + + resp, err = svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(nil)}) + require.NoError(t, err) + require.IsType(t, oapi.PutTelemetry200JSONResponse{}, resp) + _, err = svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}}, + }}) + require.NoError(t, err) + assert.Equal(t, 1, st.startCount(), "the sink opens at most once per process") + }) + + t.Run("storage off after the sink opened is rejected", func(t *testing.T) { + svc, st := newStorageService(t) + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(nil)}) + require.NoError(t, err) + require.True(t, st.EverStarted()) + publishNetwork(t, svc) + before := svc.telemetrySession.Config() + seq := svc.eventStream.Seq() + appliedAt := svc.telemetrySession.AppliedAt() + + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}}, + Storage: &oapi.BrowserTelemetryStorageConfig{Enabled: &fa}, + }}) + require.NoError(t, err) + r409, ok := resp.(oapi.PutTelemetry409JSONResponse) + require.True(t, ok, "expected 409, got %T", resp) + assert.Equal(t, storageDisableConflict, r409.Message) + + after := svc.telemetrySession.Config() + assert.ElementsMatch(t, before.Categories, after.Categories, "a 409 must not change the categories") + assert.True(t, after.StoreS2, "a 409 must not change the storage toggle") + assert.Equal(t, appliedAt, svc.telemetrySession.AppliedAt(), "a 409 must not re-apply the session") + assert.Equal(t, seq, svc.eventStream.Seq()) + assert.True(t, st.Running(), "the sink keeps running") + assert.True(t, svc.telemetrySession.Active()) + + // A clear that carries storage off is refused the same way: the request + // asks for a state this instance can no longer honor. + resp, err = svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: cleared(&fa)}) + require.NoError(t, err) + require.IsType(t, oapi.PutTelemetry409JSONResponse{}, resp) + assert.True(t, svc.telemetrySession.Active(), "a refused clear leaves the session running") + + // A clear that leaves storage alone still works. + resp, err = svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: cleared(nil)}) + require.NoError(t, err) + require.IsType(t, oapi.PutTelemetry200JSONResponse{}, resp) + assert.False(t, svc.telemetrySession.Active()) + assert.True(t, st.Running(), "the handler never stops the sink") + }) + + t.Run("patch off after the sink opened is rejected", func(t *testing.T) { + svc, st := newStorageService(t) + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(nil)}) + require.NoError(t, err) + require.True(t, st.EverStarted()) + + resp, err := svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{ + Storage: &oapi.BrowserTelemetryStorageConfig{Enabled: &fa}, + }}) + require.NoError(t, err) + r409, ok := resp.(oapi.PatchTelemetry409JSONResponse) + require.True(t, ok, "expected 409, got %T", resp) + assert.Equal(t, storageDisableConflict, r409.Message) + assert.True(t, svc.telemetrySession.Config().StoreS2) + + resp, err = svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{ + Browser: allCategoriesDisabled(), + Storage: &oapi.BrowserTelemetryStorageConfig{Enabled: &fa}, + }}) + require.NoError(t, err) + require.IsType(t, oapi.PatchTelemetry409JSONResponse{}, resp) + assert.True(t, svc.telemetrySession.Active(), "a refused clearing patch leaves the session running") + + // Omitting storage keeps it on and is not a conflict. + resp, err = svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}}, + }}) + require.NoError(t, err) + r200, ok := resp.(oapi.PatchTelemetry200JSONResponse) + require.True(t, ok, "expected 200, got %T", resp) + assert.True(t, *r200.Config.Storage.Enabled) + }) + + t.Run("a clear does not open the sink", func(t *testing.T) { + svc, st := newStorageService(t) + + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: cleared(nil)}) + require.NoError(t, err) + require.IsType(t, oapi.PutTelemetry200JSONResponse{}, resp) + resp, err = svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: cleared(&fa)}) + require.NoError(t, err) + require.IsType(t, oapi.PutTelemetry200JSONResponse{}, resp, "storage off is fine while nothing has started") + assert.Equal(t, 0, st.startCount()) + assert.False(t, st.EverStarted()) + }) + + t.Run("the first storing session stores from its start", func(t *testing.T) { + svc, st := newStorageService(t) + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(nil)}) + require.NoError(t, err) + assert.Zero(t, st.afterSeq, "nothing precedes the first session, so nothing is skipped") + }) + + t.Run("a session that never stored can start storing, without what it captured", func(t *testing.T) { + svc, st := newStorageService(t) + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(&fa)}) + require.NoError(t, err) + publishNetwork(t, svc) + publishNetwork(t, svc) + require.Equal(t, 0, st.startCount()) + + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(nil)}) + require.NoError(t, err) + require.IsType(t, oapi.PutTelemetry200JSONResponse{}, resp) + assert.Equal(t, 1, st.startCount()) + assert.EqualValues(t, 2, st.afterSeq, "events captured with storage off must not be stored") + }) + + t.Run("a patch that turns storage on skips what was captured before it", func(t *testing.T) { + svc, st := newStorageService(t) + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(&fa)}) + require.NoError(t, err) + publishNetwork(t, svc) + + _, err = svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{ + Storage: &oapi.BrowserTelemetryStorageConfig{Enabled: &tr}, + }}) + require.NoError(t, err) + assert.Equal(t, 1, st.startCount()) + assert.EqualValues(t, 1, st.afterSeq) + }) + + t.Run("a storing session after a cleared storage-off one skips it", func(t *testing.T) { + svc, st := newStorageService(t) + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(&fa)}) + require.NoError(t, err) + publishNetwork(t, svc) + publishNetwork(t, svc) + publishNetwork(t, svc) + _, err = svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: cleared(&fa)}) + require.NoError(t, err) + + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(nil)}) + require.NoError(t, err) + require.IsType(t, oapi.PutTelemetry201JSONResponse{}, resp) + assert.EqualValues(t, 3, st.afterSeq) + }) + + t.Run("a retried start keeps the session's floor", func(t *testing.T) { + svc, st := newStorageService(t) + st.startErr = errors.New("basin unreachable") + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(nil)}) + require.NoError(t, err) + publishNetwork(t, svc) + + st.mu.Lock() + st.startErr = nil + st.mu.Unlock() + _, err = svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(nil)}) + require.NoError(t, err) + assert.Zero(t, st.afterSeq, "events captured while storage was on but failing to open are still stored") + }) + + t.Run("a failed start is retried by the next request", func(t *testing.T) { + svc, st := newStorageService(t) + st.startErr = errors.New("basin unreachable") + + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(nil)}) + require.NoError(t, err) + require.IsType(t, oapi.PutTelemetry201JSONResponse{}, resp, "storage is best-effort") + assert.False(t, st.EverStarted()) + + st.mu.Lock() + st.startErr = nil + st.mu.Unlock() + _, err = svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}}, + }}) + require.NoError(t, err) + assert.Equal(t, 1, st.startCount()) + }) + + t.Run("no controller means no storage and no conflict", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) // s2Storage left nil + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(nil)}) + require.NoError(t, err) + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(&fa)}) + require.NoError(t, err) + require.IsType(t, oapi.PutTelemetry200JSONResponse{}, resp) + }) + + t.Run("storage off waits for a start in flight", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + st := &blockingStartS2Storage{entered: make(chan struct{}), release: make(chan struct{})} + svc.s2Storage = st + + // The PUT that turns storage on does not return until its deferred + // reconcile has run Start, which this stub holds open. + onDone := make(chan oapi.PutTelemetryResponseObject, 1) + go func() { + resp, _ := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(nil)}) + onDone <- resp + }() + select { + case <-st.entered: + case <-time.After(2 * time.Second): + t.Fatal("PUT did not reach the storage start") + } + + // A storage-off request arriving now must not slip past the guard: it + // waits for the start to settle, then sees it and refuses. + offDone := make(chan oapi.PutTelemetryResponseObject, 1) + go func() { + resp, _ := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: networkOn(&fa)}) + offDone <- resp + }() + select { + case resp := <-offDone: + t.Fatalf("storage-off request returned %T while the sink was still opening", resp) + case <-time.After(100 * time.Millisecond): + } + + close(st.release) + require.IsType(t, oapi.PutTelemetry201JSONResponse{}, <-onDone) + require.IsType(t, oapi.PutTelemetry409JSONResponse{}, <-offDone) + assert.True(t, svc.telemetrySession.Config().StoreS2, "the committed config never said off while the sink was open") + }) +} + +// blockingStartS2Storage holds Start open until released, to prove the guard +// waits on storageMu rather than reading EverStarted mid-start. +type blockingStartS2Storage struct { + mu sync.Mutex + entered chan struct{} + release chan struct{} + everStarted bool +} + +func (s *blockingStartS2Storage) Start(context.Context, uint64) error { + close(s.entered) + <-s.release + s.mu.Lock() + defer s.mu.Unlock() + s.everStarted = true + return nil +} + +func (s *blockingStartS2Storage) Stop(context.Context) error { return nil } +func (s *blockingStartS2Storage) Running() bool { return s.EverStarted() } +func (s *blockingStartS2Storage) EverStarted() bool { + s.mu.Lock() + defer s.mu.Unlock() + return s.everStarted +} + +// gatedCdpMonitor holds the first optional-capture enable open until released, +// then fails it, so a test can act while a PUT or PATCH is mid-apply and then +// drive its rollback. +type gatedCdpMonitor struct { + stubCdpMonitor + once sync.Once + entered chan struct{} + release chan struct{} +} + +func (g *gatedCdpMonitor) SetTelemetry(enabled bool) error { + if !enabled { + return nil + } + gated := false + g.once.Do(func() { gated = true }) + if !gated { + return nil + } + close(g.entered) + <-g.release + return errors.New("collector configuration failed") +} + +func TestTelemetryStorageNeverReadsAnUnsettledConfig(t *testing.T) { + ctx := context.Background() + tr, fa := true, false + for _, method := range []string{"PUT", "PATCH"} { + t.Run(method, func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + st := &stubS2Storage{} + svc.s2Storage = st + // A non-CDP session with storage off, so the collector is not needed yet. + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{System: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}}, + Storage: &oapi.BrowserTelemetryStorageConfig{Enabled: &fa}, + }}) + require.NoError(t, err) + + // Turn storage on together with a CDP category whose capture will + // fail, so the request commits provisionally and then rolls back. + gate := &gatedCdpMonitor{entered: make(chan struct{}), release: make(chan struct{})} + svc.cdpMonitor = gate + on := &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{Network: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}}, + Storage: &oapi.BrowserTelemetryStorageConfig{Enabled: &tr}, + } + done := make(chan any, 1) + go func() { + if method == "PATCH" { + resp, _ := svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{Body: on}) + done <- resp + return + } + resp, _ := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: on}) + done <- resp + }() + select { + case <-gate.entered: + case <-time.After(2 * time.Second): + t.Fatal("request did not reach the capture apply") + } + + // A reconcile deferred from an earlier request runs now. It must wait + // for this request to settle rather than read storage-on mid-apply. + reconciled := make(chan struct{}) + go func() { + svc.reconcileStorage(ctx) + close(reconciled) + }() + select { + case <-reconciled: + t.Fatal("reconcileStorage ran while the config was provisional") + case <-time.After(100 * time.Millisecond): + } + + close(gate.release) + resp := <-done + if method == "PATCH" { + require.IsType(t, oapi.PatchTelemetry500JSONResponse{}, resp) + } else { + require.IsType(t, oapi.PutTelemetry500JSONResponse{}, resp) + } + <-reconciled + assert.False(t, svc.telemetrySession.Config().StoreS2, "the failed request rolled storage back off") + assert.Equal(t, 0, st.startCount(), "the sink must not open for a config that was rolled back") + }) + } +} diff --git a/server/cmd/api/fork_identity_test.go b/server/cmd/api/fork_identity_test.go index a83453804..f4a4cf3b9 100644 --- a/server/cmd/api/fork_identity_test.go +++ b/server/cmd/api/fork_identity_test.go @@ -1,7 +1,6 @@ package main import ( - "context" "encoding/json" "log/slog" "net/http" @@ -212,10 +211,11 @@ func TestAppliedS2StreamPrefersForkIdentity(t *testing.T) { markForkIdentityWaitArmed(t) cfg := &config.Config{S2Stream: "seed-stream"} - // Nothing applied yet: the env this process started with is all there is. - stream, applied := appliedS2Stream(cfg) - assert.False(t, applied) - assert.Equal(t, "seed-stream", stream) + // Nothing applied yet: the env this process started with belongs to the + // parent, so there is nothing this instance may bind. + stream, pending := appliedS2Stream(cfg) + assert.True(t, pending) + assert.Empty(t, stream) // A payload written but not yet applied is not the instance's identity. writeForkIdentityPayloadForTest(t, forkidentity.Payload{ @@ -223,17 +223,33 @@ func TestAppliedS2StreamPrefersForkIdentity(t *testing.T) { "session_intel_url": "https://intel.example.test", "s2_stream": "fork-stream", }) - stream, applied = appliedS2Stream(cfg) - assert.False(t, applied) - assert.Equal(t, "seed-stream", stream) + stream, pending = appliedS2Stream(cfg) + assert.True(t, pending) + assert.Empty(t, stream) // Once applied it survives a restart of this process, which is the point. require.NoError(t, forkidentity.WriteAppliedMarker("fork")) - stream, applied = appliedS2Stream(cfg) - assert.True(t, applied) + stream, pending = appliedS2Stream(cfg) + assert.False(t, pending) assert.Equal(t, "fork-stream", stream) } +func TestAppliedS2StreamNeverFallsBackToSeedOnFork(t *testing.T) { + useTempForkIdentityFiles(t) + markForkIdentityWaitArmed(t) + cfg := &config.Config{S2Stream: "seed-stream"} + + writeForkIdentityPayloadForTest(t, forkidentity.Payload{ + "instance_name": "fork", + "session_intel_url": "https://intel.example.test", + }) + require.NoError(t, forkidentity.WriteAppliedMarker("fork")) + + stream, pending := appliedS2Stream(cfg) + assert.False(t, pending) + assert.Empty(t, stream, "an applied fork identity without a stream must not bind the parent's") +} + func TestAppliedS2StreamIgnoresMarkerForAnotherInstance(t *testing.T) { useTempForkIdentityFiles(t) markForkIdentityWaitArmed(t) @@ -246,9 +262,9 @@ func TestAppliedS2StreamIgnoresMarkerForAnotherInstance(t *testing.T) { }) require.NoError(t, forkidentity.WriteAppliedMarker("someone-else")) - stream, applied := appliedS2Stream(cfg) - assert.False(t, applied) - assert.Equal(t, "seed-stream", stream) + stream, pending := appliedS2Stream(cfg) + assert.True(t, pending) + assert.Empty(t, stream) } func TestAppliedS2StreamIgnoresMarkerFromBeforeThisBoot(t *testing.T) { @@ -264,32 +280,34 @@ func TestAppliedS2StreamIgnoresMarkerFromBeforeThisBoot(t *testing.T) { }) require.NoError(t, forkidentity.WriteAppliedMarker("stale-fork")) - stream, applied := appliedS2Stream(cfg) - assert.False(t, applied) + stream, pending := appliedS2Stream(cfg) + assert.False(t, pending) assert.Equal(t, "seed-stream", stream) } func TestS2StreamResolverUsesHookPayloadWithoutReadyFile(t *testing.T) { useTempForkIdentityFiles(t) - resolver := newS2StreamResolver(&config.Config{S2Stream: "seed-stream"}) - started := make(chan string, 1) - - resolver.StartForAppliedPayload( - context.Background(), - forkidentity.Payload{"s2_stream": "fork-stream"}, - func(context.Context) error { - started <- resolver.Resolve() - return nil - }, - slog.Default(), - ) + resolver := newS2StreamResolver(&config.Config{S2Stream: "seed-stream"}, slog.Default()) - select { - case stream := <-started: - assert.Equal(t, "fork-stream", stream) - case <-time.After(time.Second): - t.Fatal("S2 storage did not start") - } + resolver.RecordAppliedPayload(forkidentity.Payload{"s2_stream": "fork-stream"}) + + assert.Equal(t, "fork-stream", resolver.Resolve()) +} + +// A telemetry config applied to a fork before its identity must not bind the +// parent's stream: the sink stays closed until the handoff has happened. +func TestS2StreamResolverHoldsBackSeedStreamWhilePending(t *testing.T) { + useTempForkIdentityFiles(t) + markForkIdentityWaitArmed(t) + resolver := newS2StreamResolver(&config.Config{S2Stream: "seed-stream"}, slog.Default()) + + assert.Empty(t, resolver.Resolve()) + + resolver.RecordAppliedPayload(forkidentity.Payload{"instance_name": "fork", "session_intel_url": "https://intel.example.test"}) + assert.Empty(t, resolver.Resolve(), "a fork payload without a stream must not fall back to the parent's") + + resolver.RecordAppliedPayload(forkidentity.Payload{"s2_stream": "fork-stream"}) + assert.Equal(t, "fork-stream", resolver.Resolve()) } // markForkIdentityWaitArmed stands in for the wrapper having entered the wait, diff --git a/server/cmd/api/main.go b/server/cmd/api/main.go index 30285c728..0d2c2cbb4 100644 --- a/server/cmd/api/main.go +++ b/server/cmd/api/main.go @@ -129,31 +129,14 @@ func main() { slogger.Error("sysmon: kmsg OOM monitor disabled", "err", err) } - // Malformed flag: treat as disabled but surface it, matching how the OTLP - // identity provider handles the same flag. Exiting would crashloop the VM - // over a provisioning typo. - forkIdentityWait, err := forkidentity.WaitEnabled() - if err != nil { - slogger.Warn("fork-identity wait flag invalid; treating as disabled", "err", err) - } - // An instance that already took a fork identity keeps it across a restart of - // this process, which the env captured at boot does not reflect. - _, s2StreamApplied := appliedS2Stream(config) - - // Optional S2 storage sink. The append session is bound to one stream when - // it starts, and an instance still holding for a fork identity is carrying - // the stream name of the instance it was forked from, so defer the writer - // until the identity that owns the events arrives. - s2Streams := newS2StreamResolver(config) + // Optional S2 storage sink. Constructed here but opened by the telemetry + // handler, with the first capture session that has storage on, so an + // instance whose sessions keep storage off never opens an append session. + // The session binds one stream when it opens, and an instance still holding + // for a fork identity is carrying the stream of the instance it was forked + // from, so the resolver hands out no stream until that identity arrives. + s2Streams := newS2StreamResolver(config, slogger) s2Storage := events.NewS2StorageController(eventStream, config.S2Basin, config.S2AccessToken, s2Streams.Resolve, events.S2Config{}, slogger) - if !forkIdentityWait || s2StreamApplied { - // An optional sink that cannot open must not take the browser down: the - // api runs under supervisord with autorestart, so exiting here would - // crashloop the VM over a misconfigured basin or token. - if err := s2Storage.Start(ctx); err != nil { - slogger.Error("failed to start S2 storage writer, continuing without it", "err", err) - } - } // Optional OTLP export sink. Independent of S2; both can run together. // Constructed when an endpoint is provisioned, but left stopped: export is @@ -194,15 +177,15 @@ func main() { }, slogger) } - // A fork boots carrying the stream of the instance it came from, and the S2 - // writer binds a stream when it starts, so it is opened here once the guest - // has taken an identity of its own. OTLP needs no hook here: its credential - // resolves per request and its resource attributes at exporter build, and - // export is turned on per session, which the platform does after the - // handoff. An export started before then keeps the source's resource - // attributes until it is restarted. + // A fork boots carrying the stream of the instance it came from, so the + // hook records the stream of the identity the guest has taken for the S2 + // writer to bind when the telemetry handler opens it, which the platform + // does after the handoff. OTLP needs no hook here: its credential resolves + // per request and its resource attributes at exporter build, and export is + // turned on per session, likewise after the handoff. An export started + // before then keeps the source's resource attributes until it is restarted. onForkIdentityApplied := func(payload forkidentity.Payload) { - s2Streams.StartForAppliedPayload(ctx, payload, s2Storage.Start, slogger) + s2Streams.RecordAppliedPayload(payload) } apiService, err := api.New( @@ -438,7 +421,7 @@ func main() { // S2 storage shuts down after the servers above, since they might produce // events we want to capture into the stream; we must let them finish before - // closing the writer. + // closing the writer. A no-op when no session ever opened it. s2StopCtx, s2StopCancel := context.WithTimeout(context.Background(), 10*time.Second) defer s2StopCancel() if err := s2Storage.Stop(s2StopCtx); err != nil { @@ -465,68 +448,70 @@ func mustFFmpeg() { } // s2StreamResolver uses the hook payload once applied and the persisted payload -// after an API process restart. +// after an API process restart. Resolve is called by the S2 controller when the +// telemetry handler opens the sink, which on a fork happens after the handoff. type s2StreamResolver struct { cfg *config.Config + log *slog.Logger hookStream atomic.Pointer[string] } -func newS2StreamResolver(cfg *config.Config) *s2StreamResolver { - return &s2StreamResolver{cfg: cfg} +func newS2StreamResolver(cfg *config.Config, log *slog.Logger) *s2StreamResolver { + return &s2StreamResolver{cfg: cfg, log: log} } +// Resolve returns the stream the S2 writer may bind, or "" while the instance +// is still waiting for a fork identity, which keeps the sink closed rather than +// bound to the parent's stream. func (r *s2StreamResolver) Resolve() string { if stream := r.hookStream.Load(); stream != nil { return *stream } - stream, _ := appliedS2Stream(r.cfg) + stream, pending := appliedS2Stream(r.cfg) + if pending { + r.log.Warn("S2 storage not opened: fork identity pending; it opens with the next telemetry config applied after the handoff") + } return stream } -func (r *s2StreamResolver) StartForAppliedPayload( - ctx context.Context, - payload forkidentity.Payload, - start func(context.Context) error, - log *slog.Logger, -) { - stream := forkidentity.FirstNonEmpty(forkidentity.Env(payload)["S2_STREAM"], r.cfg.S2Stream) +// RecordAppliedPayload records the applied identity's stream in-process so a +// resolve that follows the handoff does not depend on re-reading the identity +// files. The hook only runs on a fork, whose boot S2_STREAM is the parent's, +// so a payload without a stream leaves storage closed rather than falling back +// to it. It does not block: the handler contract forbids holding up the handoff. +func (r *s2StreamResolver) RecordAppliedPayload(payload forkidentity.Payload) { + stream := forkidentity.Env(payload)["S2_STREAM"] r.hookStream.Store(&stream) - // The handler contract forbids blocking the handoff on an optional sink. - go func() { - if err := start(ctx); err != nil { - log.Error("failed to start S2 storage writer for fork identity", "err", err) - } - }() } // appliedS2Stream resolves the stream the S2 writer should bind, preferring a // fork identity the guest has already taken over the env this process started // with. The env belongs to the instance this one was forked from, and it is what // a restarted api would otherwise bind for the rest of the instance's life. -// Reports whether an applied fork identity supplied it. +// While the wait is armed and no identity has been applied there is no stream +// this instance may bind yet, so the result is empty; pending reports that case. +// Once one is applied only its own stream counts, never the boot env's. // // OTLP resolves its identity per use instead (otlpIdentityProvider), so a stale // read there self-corrects; an S2 append session binds once, so this read has to // be right the first time. -func appliedS2Stream(cfg *config.Config) (string, bool) { - stream := cfg.S2Stream - +func appliedS2Stream(cfg *config.Config) (stream string, pending bool) { // The wrapper clears stale identity state and writes the ready file before // starting this process. Its presence distinguishes a fork-wait boot; once // the fork is applied, the marker and payload survive API restarts and must // take precedence over the seed identity in the boot environment. if _, err := os.Stat(forkidentity.ReadyFile); err != nil { - return stream, false + return cfg.S2Stream, false } applied, err := forkidentity.ReadAppliedMarker() if err != nil || applied == "" { - return stream, false + return "", true } payload, err := forkidentity.ReadPayload() if err != nil || payload.InstanceName() != applied { - return stream, false + return "", true } - return forkidentity.FirstNonEmpty(forkidentity.Env(payload)["S2_STREAM"], stream), true + return forkidentity.Env(payload)["S2_STREAM"], false } // chromeJSONProxyHandler returns a handler that proxies a JSON endpoint from diff --git a/server/lib/events/s2storage.go b/server/lib/events/s2storage.go index 701b7c571..92222d741 100644 --- a/server/lib/events/s2storage.go +++ b/server/lib/events/s2storage.go @@ -152,29 +152,32 @@ func (s *s2Storage) Close(ctx context.Context) error { return err } -// S2StorageWriter reads from an EventStream and forwards each event to S2. -// Construct with NewS2StorageWriter, call Start to begin, Stop to drain and shut down. +// S2StorageWriter reads from an EventStream and forwards each event after +// afterSeq to S2. Construct with NewS2StorageWriter, call Start to begin, Stop +// to drain and shut down. type S2StorageWriter struct { es *EventStream basin string accessToken string streamName string + afterSeq uint64 cfg S2Config log *slog.Logger mu sync.Mutex started bool - storage *s2Storage + storage Storage writer *StorageWriter done chan struct{} } -func NewS2StorageWriter(es *EventStream, basin, accessToken, streamName string, cfg S2Config, log *slog.Logger) *S2StorageWriter { +func NewS2StorageWriter(es *EventStream, basin, accessToken, streamName string, afterSeq uint64, cfg S2Config, log *slog.Logger) *S2StorageWriter { return &S2StorageWriter{ es: es, basin: basin, accessToken: accessToken, streamName: streamName, + afterSeq: afterSeq, cfg: cfg, log: log, } @@ -193,8 +196,14 @@ func (w *S2StorageWriter) Start(ctx context.Context) error { if err != nil { return err } + w.startLocked(ctx, storage) + return nil +} + +// startLocked begins forwarding to an opened storage. Requires w.mu. +func (w *S2StorageWriter) startLocked(ctx context.Context, storage Storage) { w.storage = storage - w.writer = NewStorageWriter(w.es, storage, w.log) + w.writer = NewStorageWriterAfter(w.es, storage, w.log, w.afterSeq) w.done = make(chan struct{}) w.started = true go func() { @@ -203,7 +212,6 @@ func (w *S2StorageWriter) Start(ctx context.Context) error { w.log.Error("s2 storage writer failed", "err", err) } }() - return nil } // Stop waits for the Run goroutine to exit, drains any remaining ring events, @@ -256,7 +264,11 @@ func NewS2StorageController(es *EventStream, basin, token string, streamFn func( return &S2StorageController{es: es, basin: basin, token: token, streamFn: streamFn, cfg: cfg, log: log} } -func (c *S2StorageController) Start(parent context.Context) (err error) { +// Start opens the writer, which stores only events published after afterSeq: +// the ring still holds whatever was captured before storage was wanted, and +// that must not be persisted. afterSeq applies to the call that opens the +// writer; once one has, later calls are no-ops. +func (c *S2StorageController) Start(parent context.Context, afterSeq uint64) (err error) { c.mu.Lock() if c.everStarted { c.mu.Unlock() @@ -290,7 +302,7 @@ func (c *S2StorageController) Start(parent context.Context) (err error) { return nil } runCtx, cancel := context.WithCancel(parent) - w := NewS2StorageWriter(c.es, c.basin, c.token, stream, c.cfg, c.log) + w := NewS2StorageWriter(c.es, c.basin, c.token, stream, afterSeq, c.cfg, c.log) if err = w.Start(runCtx); err != nil { cancel() return err diff --git a/server/lib/events/s2storage_test.go b/server/lib/events/s2storage_test.go index f7e63aace..6b71b75b8 100644 --- a/server/lib/events/s2storage_test.go +++ b/server/lib/events/s2storage_test.go @@ -41,11 +41,11 @@ func TestS2StorageController_OpensNothingUntilStart(t *testing.T) { func TestS2StorageController_StartIsIdempotent(t *testing.T) { c, resolved := newTestController(t, "test-stream") - require.NoError(t, c.Start(context.Background())) + require.NoError(t, c.Start(context.Background(), 0)) first := c.writer require.NotNil(t, first) - require.NoError(t, c.Start(context.Background())) + require.NoError(t, c.Start(context.Background(), 0)) assert.Same(t, first, c.writer, "second Start must not replace the writer") assert.Equal(t, int32(1), resolved.Load(), "second Start must not re-resolve the stream") @@ -61,7 +61,7 @@ func TestS2StorageController_ConcurrentStartOpensOneWriter(t *testing.T) { wg.Add(1) go func() { defer wg.Done() - errs <- c.Start(context.Background()) + errs <- c.Start(context.Background(), 0) }() } wg.Wait() @@ -89,11 +89,11 @@ func TestS2StorageController_ConcurrentStartReturnsFailure(t *testing.T) { cancel() firstDone := make(chan error, 1) - go func() { firstDone <- c.Start(ctx) }() + go func() { firstDone <- c.Start(ctx, 0) }() <-entered secondDone := make(chan error, 1) - go func() { secondDone <- c.Start(context.Background()) }() + go func() { secondDone <- c.Start(context.Background(), 0) }() select { case err := <-secondDone: t.Fatalf("concurrent Start returned before the in-flight start finished: %v", err) @@ -112,12 +112,12 @@ func TestS2StorageController_StartFailureRollsBack(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() - require.ErrorIs(t, c.Start(ctx), context.Canceled) + require.ErrorIs(t, c.Start(ctx, 0), context.Canceled) assert.False(t, c.Running()) assert.False(t, c.EverStarted()) assert.Equal(t, int32(1), resolved.Load()) - require.NoError(t, c.Start(context.Background())) + require.NoError(t, c.Start(context.Background(), 0)) assert.True(t, c.Running()) assert.True(t, c.EverStarted()) assert.Equal(t, int32(2), resolved.Load()) @@ -133,7 +133,7 @@ func TestS2StorageController_FailedStartDoesNotLogEnabled(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() - require.ErrorIs(t, c.Start(ctx), context.Canceled) + require.ErrorIs(t, c.Start(ctx, 0), context.Canceled) assert.NotContains(t, logs.String(), "S2 storage enabled") } @@ -143,7 +143,7 @@ func TestS2StorageController_SuccessfulStartLogsEnabled(t *testing.T) { return "test-stream" }, S2Config{}, slog.New(slog.NewTextHandler(&logs, nil))) - require.NoError(t, c.Start(context.Background())) + require.NoError(t, c.Start(context.Background(), 0)) assert.Contains(t, logs.String(), "S2 storage enabled") require.NoError(t, stopController(t, c)) @@ -152,7 +152,7 @@ func TestS2StorageController_SuccessfulStartLogsEnabled(t *testing.T) { func TestS2StorageController_EmptyStreamDoesNotStart(t *testing.T) { c, resolved := newTestController(t, "") - require.NoError(t, c.Start(context.Background())) + require.NoError(t, c.Start(context.Background(), 0)) assert.Equal(t, int32(1), resolved.Load()) assert.False(t, c.Running()) assert.False(t, c.EverStarted()) @@ -165,7 +165,7 @@ func TestS2StorageController_MissingCredentialsDoesNotResolveStream(t *testing.T return "test-stream" }, S2Config{}, slog.Default()) - require.NoError(t, c.Start(context.Background())) + require.NoError(t, c.Start(context.Background(), 0)) assert.Zero(t, resolved.Load()) assert.False(t, c.Running()) assert.False(t, c.EverStarted()) @@ -208,7 +208,7 @@ func TestS2StorageController_StopHonorsContextDuringStart(t *testing.T) { cancelParent() startDone := make(chan error, 1) - go func() { startDone <- c.Start(parent) }() + go func() { startDone <- c.Start(parent, 0) }() <-entered ctx, cancel := context.WithCancel(context.Background()) @@ -298,13 +298,58 @@ func TestS2StorageController_StopHonorsContextDuringStop(t *testing.T) { func TestS2StorageController_EverStartedSurvivesStop(t *testing.T) { c, resolved := newTestController(t, "test-stream") - require.NoError(t, c.Start(context.Background())) + require.NoError(t, c.Start(context.Background(), 0)) require.NoError(t, stopController(t, c)) assert.False(t, c.Running()) assert.True(t, c.EverStarted()) - require.NoError(t, c.Start(context.Background())) + require.NoError(t, c.Start(context.Background(), 0)) assert.False(t, c.Running(), "a stopped controller must not reopen the sink") assert.Equal(t, int32(1), resolved.Load()) } + +// The ring can still hold events captured before storage was wanted, and the +// writer must not replay them. A ring of four holding seqs 7..10 makes a +// replay observable without submitting anything: a writer after seq 10 has +// nothing to read, while one reading from seq 0 finds six evicted events. +func TestS2StorageController_StartSkipsEventsAtOrBeforeAfterSeq(t *testing.T) { + es := newTestStream(t, 4) + for range 10 { + es.Publish(Envelope{Event: makeEvent("captured with storage off")}) + } + c := NewS2StorageController(es, "test-basin", "test-token", func() string { return "test-stream" }, S2Config{}, slog.Default()) + + require.NoError(t, c.Start(context.Background(), 10)) + require.True(t, c.Running()) + assert.Never(t, func() bool { return es.DroppedEvents() > 0 }, 200*time.Millisecond, time.Millisecond, "the writer read events at or before afterSeq") + + require.NoError(t, stopController(t, c)) +} + +// The first event the writer forwards is exactly the one after afterSeq, so +// nothing at the boundary captured with storage off is persisted. +func TestS2StorageWriter_ForwardsFromAfterSeq(t *testing.T) { + es := newTestStream(t, 64) + for range 10 { + es.Publish(Envelope{Event: makeEvent("ev")}) + } + backend := &mockBackend{} + w := NewS2StorageWriter(es, "", "", "", 7, S2Config{}, slog.Default()) + ctx, cancel := context.WithCancel(context.Background()) + w.mu.Lock() + w.startLocked(ctx, backend) + w.mu.Unlock() + + require.Eventually(t, func() bool { return len(backend.envelopes()) == 3 }, time.Second, time.Millisecond) + cancel() + stopCtx, stopCancel := context.WithTimeout(context.Background(), 5*time.Second) + defer stopCancel() + require.NoError(t, w.Stop(stopCtx)) + + var seqs []uint64 + for _, env := range backend.envelopes() { + seqs = append(seqs, env.Seq) + } + assert.Equal(t, []uint64{8, 9, 10}, seqs) +} diff --git a/server/lib/oapi/oapi.go b/server/lib/oapi/oapi.go index 8921b1a98..6cc696faa 100644 --- a/server/lib/oapi/oapi.go +++ b/server/lib/oapi/oapi.go @@ -6154,6 +6154,9 @@ type BrowserTelemetryConfig struct { // Export Forwarding of captured telemetry to an external destination. Independent of what is captured: export is off unless explicitly enabled here, even when an export destination is configured. In a PUT (full replace) an omitted export block resets export to off, the same as omitted categories turn off; in a PATCH an omitted field leaves the current setting unchanged. Export *BrowserTelemetryExportConfig `json:"export,omitempty"` + + // Storage Durable storage of captured telemetry in Kernel's event store. On by default, so a caller that omits this block keeps today's behavior: in a PUT (full replace) an omitted block means enabled, unlike `export`, whose omitted block means off; in a PATCH an omitted field leaves the current setting unchanged. Only events captured while storage is enabled are stored. Storage opens with the first capture session that has it enabled and stays open for the life of the instance, so once it has started a request that disables it is rejected with 409. + Storage *BrowserTelemetryStorageConfig `json:"storage,omitempty"` } // BrowserTelemetryControlConfig Configuration for the control category. Same `enabled` semantics as any other category, plus settings for the browser-control commands the CDP proxy reports. @@ -6177,6 +6180,12 @@ type BrowserTelemetryOTLPExportConfig struct { Enabled *bool `json:"enabled,omitempty"` } +// BrowserTelemetryStorageConfig Durable storage of captured telemetry in Kernel's event store. On by default, so a caller that omits this block keeps today's behavior: in a PUT (full replace) an omitted block means enabled, unlike `export`, whose omitted block means off; in a PATCH an omitted field leaves the current setting unchanged. Only events captured while storage is enabled are stored. Storage opens with the first capture session that has it enabled and stays open for the life of the instance, so once it has started a request that disables it is rejected with 409. +type BrowserTelemetryStorageConfig struct { + // Enabled Whether captured telemetry is written to Kernel's event store. Defaults to true. Set false to keep events on the live stream and any enabled export destination only. Cannot be set false on an instance that has already started storage (409). + Enabled *bool `json:"enabled,omitempty"` +} + // ChromiumConfigureError Failure from batched chromium configure — includes which phase failed. type ChromiumConfigureError struct { Message string `json:"message"` @@ -15054,6 +15063,7 @@ type PatchTelemetryResponse struct { JSON200 *TelemetryState JSON400 *BadRequestError JSON404 *NotFoundError + JSON409 *ConflictError JSON500 *InternalError } @@ -15079,6 +15089,7 @@ type PutTelemetryResponse struct { JSON200 *TelemetryState JSON201 *TelemetryState JSON400 *BadRequestError + JSON409 *ConflictError JSON500 *InternalError } @@ -18108,6 +18119,13 @@ func ParsePatchTelemetryResponse(rsp *http.Response) (*PatchTelemetryResponse, e } response.JSON404 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: + var dest ConflictError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON409 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalError if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -18155,6 +18173,13 @@ func ParsePutTelemetryResponse(rsp *http.Response) (*PutTelemetryResponse, error } response.JSON400 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: + var dest ConflictError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON409 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalError if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -22619,6 +22644,15 @@ func (response PatchTelemetry404JSONResponse) VisitPatchTelemetryResponse(w http return json.NewEncoder(w).Encode(response) } +type PatchTelemetry409JSONResponse struct{ ConflictErrorJSONResponse } + +func (response PatchTelemetry409JSONResponse) VisitPatchTelemetryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(409) + + return json.NewEncoder(w).Encode(response) +} + type PatchTelemetry500JSONResponse struct{ InternalErrorJSONResponse } func (response PatchTelemetry500JSONResponse) VisitPatchTelemetryResponse(w http.ResponseWriter) error { @@ -22663,6 +22697,15 @@ func (response PutTelemetry400JSONResponse) VisitPutTelemetryResponse(w http.Res return json.NewEncoder(w).Encode(response) } +type PutTelemetry409JSONResponse struct{ ConflictErrorJSONResponse } + +func (response PutTelemetry409JSONResponse) VisitPutTelemetryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(409) + + return json.NewEncoder(w).Encode(response) +} + type PutTelemetry500JSONResponse struct{ InternalErrorJSONResponse } func (response PutTelemetry500JSONResponse) VisitPutTelemetryResponse(w http.ResponseWriter) error { @@ -24837,647 +24880,648 @@ func (sh *strictHandler) GetWebMCPTools(w http.ResponseWriter, r *http.Request) // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/+z9i3IbN7YwCr8Kfv67KtJ3WrSTmWRm5Jqqo0j2F+34omPJk/lmnEOC3SCJERroAGhR", - "9FSq9kPsJ9xPcgprAX0h0WRTFye2WZWKRRLXhXXDwrr8e5CqvFCSSWsGx/8eaGYKJQ2DD9/T7C37pWTG", - "PtdaafdVqqRl0ro/aVEInlLLlXzyL6Ok+86kc5ZT99d/aDYdHA/+/0/q8Z/gr+YJjvbrr78mg4yZVPPC", - "DTI4dhMSP+Pg12RwquRU8PRjzR6mc1OfS8u0pOIjTR2mI5dM3zBNfMNk8FrZF6qU2Udax2tlCcw3cL/5", - "5ogKNp2fqrwoLdMnqWseDsqtJMu4+4qKC60Kpi13CDSlwrDVGU7IxA1F1JSkfjhCYTxDrCLslqWlZcS4", - "waXlVIjlcJAMisa4/x74Du7P9uhvdMY0y4jgxrop1kcekufwB1eSGKsKQ5Qkds7IlGtjCXOQcRNyy3Kz", - "DY5tgLjzyrk8x55fJwO7LNjgeEC1pksAqGa/lFyzbHD8z2oPP1ft1ORfDLHve60WhumTgp9SIZ7f+ANf", - "hWRKhSB2Ti3JNL9hBvYxwb4JmVOZCZaRyRK+v2ZaMnHEczpj5ogWnBjAtePqHI4cbmklAtQSciHocqH5", - "bG5JqjLmYciVTIhJNWPSzJU1hMqMpIIXE0V1RmiaMmOGxC3d4PJyKumMwTL+9opwaSyjGWE5t2RcCGqn", - "SucjWvCR29F4+F6unXhKLZspvXR/M1nmDoJ+uQ0IGqu5nDkIZtRupYIIlM9cN4f5qtQp6zkA9LzEHr8m", - "A6tL6ZabrR/ZlS4Z4VMAhFshmXImMrKghlS9SFYyh6+Gf2BE8Jxb4/DR73CilGAUUM1G8B+WQizPmbE0", - "LwiX5J3ktyTnqVaGpUpmMJoDOLWD4wGX9rs/1sNzadmMAefBb2poh+OJgHsFs60JAyb1uVUw7YnvZ/4A", - "d2AtFw6FHUkUdCkUzchUaTKu0IowN65Z5yYOtddBiQdKTDnJuXXnYhUZeyZS08Wpytg4ISktCpYRasmf", - "v/7LN2SytMwQwa+Zm1QvibJzpl0rWzr2hIAbkpPQ8YYKhxmGpKV1DImSdE41TR13nDh+TPUSyIzJzLhT", - "HQ+Hw39WOPPzeEhOJsadvdtzc063URARDSRqkEmJP47yCDL9RIU4SoVKr0lo53iqQ17kLdqtJOdC8AZq", - "+TlkmU8QkaoVjHiEJF45acAyolVp2VemXm9CJM0dTJGtIbOC7wzh1lRLOGDD2ZCMr+g1u6x40jgh4+fR", - "szqMwkGjLIuu0KGV/53wzAmlKWeaTLXKOxhraJ3zLBNsQTWLTmostWUE7j9cXV2QoIgRbAX8dxgh1BXa", - "a2xkBfLVfO1T30COjhYvLU2v15d4enZB3pbSMZohNLnSNGVEs0Izh4ZczgA2/0lv6CX0Q2FlXFtHJu5H", - "1xuEtETSHJIXjh0aUhpG3AyS5m6gVEn3MwhyTQGr7ZxKYiS9ZqOUGuCXOagVbtzTuVY5I2fs5kopYciF", - "VlalSpAF14wg64vLGCFeaIdg2xUL2M0UGifEoa7OlbGoRLTUh1VWI8pcvkbaWJvkH0yrowk1LCPYkCAV", - "kQW3c45qiuAyigfJYFpKkNuvaR5hZ42TCA2BmBLiGEZe2KXnSsBBqFRymavSVI1NFIXdanrsxjWL7AVb", - "x3eDv51ncdzDzw1yjK6u1GK9+7u3L92W3d4DN/OjTbmIEeoKhbXA3FgnTtcCSdI+7xiptVXEFYm2hoQF", - "SkIi6IQJOChYPhCVBQpEbkjNUqYkpaVhcX5XUB0uEUK8mQ6O/9lL06k5wq8/r0lfGLK1GMAkWAp8a4Zr", - "wGyQ3EZGVNh0Tk/nVAgmZ+z8LAYb+osToTWDNnOqUf1FwY8cW0lGbrjhE8GcjMUBh+REEmDgRzPNMxDU", - "6ZySlEqimdVLwFpCyVQzMyeWmmtSuIuLtY50EmIUDDyuRhzxbExyuiQTRqgxKuWg28EweSksLwQjYzcQ", - "tAT5b5xCUGiVlSnT0Bm4qb5hhFtCnQZnCCWFU6U1Aw1nMWcSVWz/BTekoNpWmO2wvFoUYQU3To6Qc0sy", - "xQyRyhIuM3eLZLgt5RidYwNKNzo6lBAM1AhgnGsI1XFSb5kphe28wFQHEeBdTagZBbWAEsdauUN6VdpU", - "5e4C5xUyJVMGx9A4x58ot54wuMGDT1pCg+I2NSwscYIjYzgF3k/UDdOOt9dLqeYd+2WOjBI3bGQs1ZZl", - "Y9DLVn7D4ccENzxhAOQbnpVUEGihYRl4dXInrdK01DVmgAYfWKXDrHqLfS9HuJ77Xo42HOj+rtR5VwrI", - "ULMDRIj73p380e3AKlfP6w43qooQWteqri1uuGb5HgFY/bHvyvX4NRk0uesd8ff87E7XnkrZr6HhLm4U", - "OKVVcCFZZVQJSR0zcS3wQuYJ3wsNh28TZheMtXlqfXHquh9crUwUmH1ErI1hzmx8DA1AcqgJiJRsZTup", - "YFQTOgV73MpSh+SF46jqmsnAqg0y2JxR6WRSdVvBRo5I/U3gGaCL+2COqMyO5kpkTpxhz/YaYH2S3vAZ", - "EDdd0OWQjKeUi1KzsIcgH41VeOfGJUvCbgvBU27rg/C78AOAons7p6Vxg7sT89szZDHngq0sRrOccsmy", - "IRk7HqFKu7KCr0zd+kiwGybIwkmfSZnNmHXLcUS9bWw6oTJTEs4IjwYxjsnM6wuqtOFEMmL4DE5+844r", - "DARrwC11wptMGCiEsJQFhzW6c8q4yblxWrpXJ+EUSulInWXD9zLgUCXMVo4tAS0CTKhCLQjVqpQOvk6b", - "Mdwy0KKM5UIQzRy7ItTpOzxDZElABsKfxgMN9CnAL44qSqEV4Di1ZDGnloH8bEHU7WRWUp05cjNlmjKG", - "qx8kFVfGfTiOjgjhaA5PdpAMqnNY59HJ4PbIjXF0Q7XE6+E/BxVHuQyjVt+8qIavvrqq5qm+Oqkn/DUZ", - "LNjEwWo0VyaiKf2gTKXSFcGSusqKQCOKavth8ILaecS+Qe285+Dk/ymBkeFFkd2monRQ3npnarH+hiGi", - "xdB3sEvAaAD4LQpmUCzh6CvOu0G5/A10q9V97PWqrXpVU8l+RHv0pgPaXZEK2BdVo9rXhs9WhUI6hJuT", - "w6/1u92V5/oOO2lp50pzSy13naCr5TmXs2ckUyAY3F31hkX0tBkt3AS089Lmr4aLuTKMZExwUNGcKMKn", - "x9RxOKoZTOMkC5WWBYmypqrBs+Go6yHByeQjU7CUT3mKT4z4nuZW7JUTb8Z+/vbtm7ej05OLq9MfTkbv", - "Xl++efm3k+9fPh8fVhZ+JVHAGbOTVflqDdZjP8z4OOgNmtlSS+CMpaFOlzRKwINfSw9bay39pg4MY2Rc", - "A8OtuqE9+X4ZzwCq2L9pEEwdijnFqqFIrehI2MTpFCkTYkVraeuOJOfZEcy5ogfgtndXBBxHQotNp/0p", - "KIdH1DhVzSmaaxYpMAK5NYPJ2uFZB5JuFOY9NYXFnGkv0r0sdJweVaGHUBY2jv/Q+sL9FATHt5vscN2y", - "7HdwzWVGNCuUO4Xwfo7IOyQXWt3wrEnQYFDChylkGDqngn9wZy+tV2QNs8eESct0oblh5IZqTqU1Dpaa", - "eXonqRKCFoaFjoxrcsO0caxtUqbXzJKDm2/IE3Lzh8OEjOFWNaIyG7lb1Rjvm2b1slWxfIOqdikFhyuQ", - "2yYuudorNWQM74bjNsnMg8rjzimczs037Y9/cOdaaul0fXdsM8YsMxakWHOhg2QAc0QJLHKEl0gO/ZQ8", - "Df4HhUX1jprr30ylay57r9P11Ok86/tohrL4Se2u3OGTQLdm19Y7TG3EBgv+Cu4aj7pJbeJuGI3xXi1n", - "ImLxgbu2l0Ekp3KJZma4UKdKmjJ3DCIvjQUJTI37BgzMfoG4vrZV3YnYgnId3lEmS0K15jdgh8gcT/zJ", - "bcIbfJLGs0aqtGaCWmYa9vjwllIpf3ElOPF29daLykyrsjDedr7pPecsaHTegqDw/fbYgbq105z63TBv", - "SeLWVK8D4HHB4fGgbj1hJOfGoANHBQdCnXaWgijUbKo0i7ywpKXGR7KgBtsOe/7vRs3/KPqOP/GOp6V7", - "KzzckAkDu5DXSl7hKeKrtyPW1pMMmJlSqvWSSIVjvnv70ivojUdCU06A7M3h4+hRa8t+CGVqO0fcq0a/", - "d9UoK05Kq6ZciFfRC+dPc57O8aTU1LuWUd+DuP85ZDqlUkmeUuHfnpGbZuzGKiXMUeF9Zv7vb7KnX/+F", - "/ekP7U2nVLu10ixzy++52ivNZzOmT1WeU5ndQdBeUsktYM84jDm0OOiYUD0rc5St9d64LEp7HGvO5YbN", - "JqTgUqJD39zawhw/eTLjdl5OhqnKn6CDUfAverI2zpOJUJMnYTA2+cOfvs6+zr77y5+/zb75LvvDX77N", - "/vTnSfbn6Z/pN99MKPhuP/Fuu6MwxtB9OyTP0Szh94YcgxuSIgzJnOLri4XnBCeGNMtoCuoeSzlQB5dE", - "8Em1ykKr2+UTh31ORXqSZsWoBt2S5iImkfxBj0CvHKWqjGni6OYCHl3YHNVQ/1zgVxzw7woo2LMGmQUs", - "BC4B9v3gLNmSCg090g8YFU5X9YRfGfKfl29eH729OCU88y8L9XKcvjRh5F8K4IecxYv/pjd1betw4gLe", - "NtAY4x0wGEkFB8nu/ieV9HK9hz6cKilZ2ukfeR7EKYLx9OyCwAmSul9rQ6jLZETJpClvs2LkOwR/hawY", - "ZdyEL4fkaqH8Jgy4kwcXPnAhCWBwx2Md56TgZQL8nxvca05vXzI5c9Lu62/+HOEIiDwblIkJTa+ZzIhU", - "WcuTx8tJvNTgK5DDe0/T3SgC/nmbJoQGTS8ycio4vCpaRb7+5s+1F655RigRSs6Yrp11QYUmmjlGU4/R", - "Dxg5s3OVNS9CqzwqylVzz/F3c9takRjrzls/BaAiQI+dhqazMQEHaiTmMdBsoFM7Z7lh4qaTZN1llxnT", - "hdbgxIe/N0+6iclwxjg3HEjLxdkjpH939V3MhvPrdSwrmpM/owbmblKfsqJSpGTKxJlawBvqg4g7P/Iw", - "bQ29Reh1ddqLvl1EX1gdBF7d2o38y3NJ3/S3YSx7ufipycXMU+ZoVm5ArnV5GPrVIrF6FflNBVmc70TF", - "2ecjItqH2E9OCGXYw4oHN2JfqYBt98JgF2GwZ66fGnPdwJ4cAXzWXKkXF7pk9hRjys0l//Cw/Mi0x+7J", - "mdZ67XnUnkd9zjxqzvhsHjGoBSog2MBhx9n5RZe1o5vTrRDUZ8LzksGCZ7G3lQps8PsWqC24zNQium0P", - "PoJN1jTjLZG4lWZYz9CXH/8EPb5XpczMQ/Pj5tj9+XG7154f7/nxl8iPkQr6cWPBpt0j3JJCGaBjNwqm", - "UiGpUjrjklpm7sTimzT62bB4q4pOKC7vCMUOseFH/bhCoxoLnEHuYtnHZV9C94hhH5cBo4e8FCwjB2N8", - "ih8nZJxzyXMnJ+ADva0/TEshEKzgfuztRT4GClMqVD4uGZtyCcakjhfze8tHLwk73QADCoYsRhWjabE8", - "FWKHHMb/xCaXCnwHgFUdo+gjM2ZsqZlJgkMxpCPJOBVqhnlHuJwlkC+AGCY8cwPf8Tor0pCcKjnls+Ca", - "HmgCw53mjJy9efXE59AgVtPplKf1WgWfaAruS6ZkVY6qyj96wuZUTCv/9ADy4Xv5RrKGD1cXTHzIOIbB", - "NMQHvvGEVsZqRvNgIDQQRpYlJFVUMJMGmeldqyCGHmfmhghwy5FiWQsePHD3Y/BdAbbMBMshxD5tgitp", - "CKxKPlaytnb/T1WGKSygeSqoMXzqk6I5melaXTNWkLIYkuduXgN5RBzk4BkbXFFWpcywWtUorHX8LEhc", - "u7lDpiEu8WNnsVohkL3DabfDKagYAKqP4GYaOZZohqFAc20f0nqpwXPU8SHXNwcZByQ0RsoaD8lzms6D", - "/xS4sHHgHQLdtGryoUWh1Y1XEtHfDWc5rt934ctSZAQ8pSgxLNXMkv/5r/8mbrMZZn1zuORlsGW3NiHv", - "3r40CdFsyrRm2iQ+uYpJiGV5Ab6gnnMW1M7ddjSdkTRc2YBhB18lvxY3pXfFEjTFPVMiQG9IHMt0dOz+", - "QA/UlJGpoLMEwlJkmYNzJrjwAbozmGOujM8B1AAnZljMaVE4VDj+9/qTfO939oivU9L1MLJ10K3vysmK", - "UbP3iKsvEEmnzaDvkBvMiUmntrrD4F1342Rw9ubVcKrSssdwZyp/4VquD2BSrYQ4l1b9jbPF+fQ1xBH3", - "GvEy2jUyBbMvuGDnTtVwf/Rb7+Vqr/bA8H1ALE1nM8DgbeNCr9NWp9iwGTeFu6G5Nl796jXy2Wq/TYP/", - "yJZ3GTt02zT0K1UadpfB646bhr9SZTq/y/B1x9jwLC8du4RGL7TKd97F884BYtPxnAHtVgy95yznq/2i", - "g0vDtL1it33Xfl51iA1nltLOmVMsLrhM5/8b9fWeQ19GO2+eBmn7rvO0em+e6IoWd52l7tqe4oLO2HDi", - "tJsr9UKrPujjunzf6BEZ0Pt61SkX+416utptw9CSFrsP7DvFhu0nGmGwdbkIQ2DOyTqV3hlcCfuN+UO0", - "b2SSEMTab9jXvvWGga7UD9xYpZfPpXV3j12GbfeNTFJoLu2Vujh70W/gC98+m0YGwyRm/QZ6y9a1IBjE", - "KRds8pJPWbpMBUPLSK8hLyM9YxNYqi1icEpNT/y8bHeKDquKl4pmvSQ3Dll16Bhu90U2+7QHvaJ6xuyQ", - "ppbfOMSAj9uHxXYnrV7RgYE8dxv1tO4SH1IzalnohU55vceO9N0wyY4Lb/SJDupUC2XuuPSzWOfoNKpg", - "MjzF9B39TaNPc9BfK2PHEjOQBgvfr8lASbaTTbOHEvZrcqfBYurijkPFVZNdB9mkNd1xb3F18o6DRXX2", - "HcfqvlnsOFA/TXbHQbfrgXcesFPhu/OIceWu/3DbbpA7jbR2d95tHVvvyf2H26So7jbKRsX0bkNFVNHd", - "BtquMu42XkxXvNsI3WrhbuOtK3G79Y9rk7uNsUEz23WgLu1p93Eiet2OSLh6hdlxDVtU4f6jbVMAdx2p", - "Q+nbeZgOFexu43TrWruOt1V523XAbn2t7zhbzc+7D3V35NxuCb7LWF0m6/5jbTD8//pz7D3oVeXLse0h", - "+/TsIjye+rf326V/8jX4YGuYDRHujRdTQ6gkdMZkrD4QZGV4tvroevbmFTyPhHfpiVLX14wV8N7tfkD3", - "rTrA/915YzYNybINzxh4HtUeZeAcAGquOe4dY95pw+20HEesjd3WzQ1W3G67d4ehvYfJdovpcpvJcaOl", - "sOtdofkmsvF5I2Yk7LTzdVjpNpvIVm1bm01UbXPQuqWpwxATt3xEzCsti+Ams1GX2SNqtYibBDZbI7bc", - "+OM39c6XxNV3wO4Xt+6HvqRfIHKTn4ELRJdfjiTs1he3874tmEklLY1VOdPk8uzHZqWxhFyURcEsY/ow", - "+A7WHo8Rrx10juGG/O3VsK/HhXdIfBCni3r3e6eLLU4XAKrHTNgaOY87ZLyvHVYj3hjeN7W7mFh/L1lu", - "om6yDX9Yd9Tj1pDj4AsWcw5Bt43asxbE8GP7y/668TzOKh+p/gyi9qtiWZ3SdQvpkwvKdZ1riuc5yzi1", - "TEAZlZRl6/7FHpIACPR1+w0YyAqA9jxkIw+pUeNx2UjsVHbnJPVq1zlJw6d9z0weoCRhg2v3qUqYM2Po", - "jG3PZYS3L2hsiGaCLlkWijGtz+uzBWZc43fx4maaURMr8PXTfLk6JtSBGJIxAn2EUdvHzUgOuFkB3eKX", - "yrAhGZcFcrRROqdyBqmT4e7GyxycWDEHImRI9m7/wS8ZMchiBsOFrAJRcDb0CdQs4DWdUS4NxqBItiBh", - "3uYSICH0+Lj6DVypidIBrqQo8wJTSONefaqNKp2B33Coqxjya7RSHpADuyzcbVMsQ7FIMy+t28LhSvKy", - "BigHyWAVUs2vYE1Qx21lRfHk0KtOwJvwypRFyKIXd9L2BLhgWLRwQXVW68GB1CalDcl3vO8zy5JWqY9f", - "SlZifYxpKQDqK17SBZU8vXaAx4ApLlPNcu/H7bNCgDt3cFH9n//6b1LKev2QJTO4dKOhAo0BUy4sVE2c", - "YEJMmkHiTI9rftmO/ohVbnvIxayyVAzJVeUYLhypKSmWz3whnWZESigsgnTZhs6QnIgFXVa1aICJQZnO", - "4ADvdkC4fYbZO5sNCs0yWtWxBCmZkAVk1vP+8BXbo4Z8YFp1BXis+5JvQov6pCuCa2KDz7eZEaliTLt5", - "8lw3XO59XkPwXEc0G6ZZMQwrGvljG69GLTRLp7lzgLg0DqgR3OeRyYOaNu5yg98SK9nk+ausueKWWwR2", - "5FnqzvGSldlkS4Rko90+JnKnpEqY6W0kVcb6ZIQ7e/NqLStcMzbNgpVknyzwc87bUdsyY7J3GybdF4OQ", - "52yaIdjMNcuVZQQ7bJ3u4yVq+jKymvRyMLiXYIjb0HtIiq6Oe9GxFx170fHYoqPj6WsvS+4iS7Rbf1ea", - "hB8wP4K3y7imreXiQaAMaSNvpsoJpEdfs9bAfJ1x+1mdS//hZrtdn+nvRE2nhvXaWgJ3Uqhc5qndYcxu", - "S1iuL+H/fLwlfDn6wibHyPupCqsj99ES1vvsFYS9grBXEB4yEb3oYfh3rQyk+6gPBAh1SF5U8es7FCzo", - "UEvWvHb2GsmXfLttYecW0RXc0uIle9zPIRXMnBp2h5ovIX8PMRZM0FNIpYDnFTz9wCxtmAWOVuWv8alX", - "itKCE6KecAv5bHz1olDEJzy7t95nMrcvaZkeJPD3mxv/pyr8N+iK2rNYjoPDK56z08ZL/gqXVoU/21fn", - "r56T8FYMlUwwPwW3LE9qV4Tzk9cnRLMZN1YvWzbyZnKkZ83eXxliyolbpi96gs8KAoudVzk6qrkbiZR+", - "o2PwDmjwHjJIBrTMuBokgxueMfcvLQrhn5BAvoCpPlcZnEteCssdZ67N+T1Py/s0ojNDHLGBVxJ8xieU", - "1C6RWcjcRLwjpvl0cJ5NaSkcuKwq0zmAsjR9gbYl2urOOmzM2XWLChvvstdg9xkjvyyTU9RP/MtNNr49", - "tvSeTGrNV78Xn4r02rOqPav6rKvNaDob7XID9rX5QZP1Xox3vAHD1E4Z3j61a9Ux9blleSNh3G6z5zxn", - "I69kexReuTVxY7lMLbHRSwEk1JvWq8J1YkTD2GnM44SMQWV2fzR05PHhkFziBcD41HndO7hvrtVkAOva", - "wQl59Y5UO3VTremygp+jfdiPGeXUXEfy33LrfYrd7UkItXAsx4Gq7koOvv5rqoplQr75q+DyOiFff/fX", - "XN2ww66zg2tsVQ951xS57YvyepLc1ZvyMRlXd1B3jOESin+rAuvi1RfR8f2T43arEOtBaB11AUE3MFtO", - "ZO7+DY3JNVvCYZwI687i1GqRkD/+9RWzNCF//uvlnE9t55l8iumkI486f+NsAW6At43c0Y7znF5ekoLf", - "MmF6P5ksNwy/vO/wXfaiBm3soIPFEqE8kAoWht5JA6s77RWwnSrzllaNNCsYjaWbnzNvmgpJoR3VzZh0", - "nBgTqV4ziCVm1LbovBHlEVS8rTKbOdSRs9pj1YtFTJF9IsT40CelrSqHw6L2Dyufi255PzEdWECHlP6R", - "LVeE9DVbnqmFdGL5mi3fFe4PTRc/+q9BSjsR8SDymZvRNVsWNNtMZo6eeJWwXZY50zwl2LOLwrgZmaVx", - "evE1W/ahYnDMxy4w4ToZNUYXyltp1wb+kS0niuqMhCZOFxBsCsqAD8b9w19lmRc0O+z/rNURnf87UVsk", - "zVkWB7TDsGY2bCTIgs5Y28a/LCp7Lg/hzONKW7yiE/fPidZqEdDzxbdO///Rrbvpxk9orcJE7gHPiJ0r", - "w1rJ6idLvGeMMP9127/+E6/1UW9rk5ipx207vpSTnFvra8sTLMBrDRNTeFrscUV8WAUrnqPsgVSsevCd", - "lKxmt72atZNTSWktstBdpRrA/Hvsvi7U8Ad4EqyUorC3g7FTMxz7cDwZS7VkmYBvgDu7PyY0vYZaLRg3", - "9BCFWhK/221M2bdC5pyphRyS10oefWBaOflHyRjesF6pG5aNSc6oRIp1N30UYd6wY+edKqDg6fV2zRPC", - "jVGbQ3hCZKCAsDZy8I2fDO5V+PXhXuf8bOyZTFga89b8QWn+QUlLhXeKJNA0wRBsQM2f5oyJce/rPU4V", - "u+Q7RpE+3ET306NrLt+hSUODFV0alnmBAh9Yjfv8lglGm194Wl7d1wOwnKny6Q5WaBzKVIEILNziSs0S", - "8hRUjN7Q3KahNrI0/U501EK5D/rOGHCB/TuO3/+KmmhtDKivFwd4uHjOxcNU//pEVVIqZ241VIwC+m3E", - "0bp9A12Pvt4NXy0XUffzCycUuLC1H9L/OfoHKQSVLEF1bqYZM7vNs+wzz9/vN8+CGxufBlI5LLhhRCvr", - "c9CtzrBOHnsr7uaUzw90yagH3+mS0ey2v2TsH9P3Bs/OBK0VpXRIamiwoqiBfyDkLHZaGXx6LrPqb6eh", - "oeCGjw/4QNlfQwsv5FOujSWwDgIqzYOrbo2knb8T1U3TjJdmy30EG3WCqX+UGE626UbyYFN5+TyiciYi", - "aPDW/77h7O+gOnyhOuMGID6UMnnlFLyuaR5bw+w5+T3VTjfkCIbcbkGC/K6suQyzIlTh3bIzx09cxT1d", - "U293II976L2d1E5QojSKZBPh9s0l+LWjzID7oRl7xYIbJxZQbPCc6qU/peaLCA1w8sQUVJ2HV7nvycd6", - "aN8xxOmhkfcrUXJPzbw7v3QvDX1T972m/jt/DngIFe4BzOl7w/mXajh/JDP579YoTibMAd0zTaAmkAyP", - "6ey5oYDA3utz7/V5Z3vhxnJz91RK1kpr9NJFIr32KsjeWPglBgmu16b5OZpyqRA0hcS8IyYjkHpbNyCa", - "yhkjTGY+XVCntaoxKOSE7jMsNNwyMPr8ukONrvUy/NxYI+Tu59VpBmBsn6Fj4fUczQXvPMuX4EcXgcdD", - "O9E1F9VHYEWLrN5XUlWD9hNRjeZ72bSXTV+kbKpLqf38xboZIxAewbN4R6a4vWLxPRlkvDhdL2bZ1XXP", - "OPeM83NmnD7j0AjTEd3ZPLWe9mjdQtXKewS72Jj56PGsVB01LOO3FsxAOjIFY9FLi89Q6l0eCbSDxzA0", - "k5DCfQmFfDp19JQKNprS1Cq9YQZoFh6QCrdwcvC+fPr0D+xr8kEpSDdw+Fm/TH9+drCdhGZnVf4Hk5qt", - "GXYUmyt993JzLzf3cvNzlZvt4s4xwVlotPBPhfuiMyYYfsZCWGVRoDjpCgTGMP8evjAVL/SZASr3MXy3", - "22BMdONnTNBltHrfmfuFTJhdMCbD2Mlaub7PyBTXofVcLnjBUNXZSdN5ZAF+65iApTLm5hl5FPZt25M9", - "I2hCvAnYYohgU7vDGqCOPXSNFIutUXNeL6haSZX8vqC+JCELwPyt9ZdksNwA3bWX8b6wLYsd5u8J2Zuw", - "mAeB6wMobnWp+0fR2urhd1TZmh33+tpeX/usvYV8icaIVA+1IoNgBy8ZKB0LueDdp7LoLea/bMWwZimf", - "j72dFr1dDhWxtFiN2Le0OPxyQgE3S8ZWjqRoAptPMeO/T+Q0SAaQx2mQDOo0ToNk4FCuZyb0pnvtGnjQ", - "xW+y4mNLaw/bTwRcTkQNkoHTqx0BQlIQBzMox5VAARzEvwXV2S6A24Baa+6Rnwy0mpkNQl79kNggfIa8", - "BuEDJDXoCbYLOmPfu6+v1AutHsjZ3w06nDRG3aKXRtrv1dG9Ovpl+SusEcEXnG3fweIUPRIuU82YNHP1", - "gKwpXR26D3+KdNozqZ2YFAJwNGFLJbPRjddDN+eL9J0Iu7UMCplVFpzQvzPtq+BFZ4lPh9JQAElJgm3u", - "qGDDLPAyu3kSSMKIL7j40LvbDB2lQ5szQJP77OJ28/i3wQH1HlMsN0+xvOcUeyn4qRUy9MDY3TxSs+EX", - "OEbEOpI7msMpiBNBzDiedjD+V8Fm44SMCznDTBMLNikeJkeUu0GMTKmnNGYljzE2J5EtvWayDhv3/dfq", - "sjmO18XtOvSJNaEVVSpUYXnOP7DRVOkuR5ewdiZTlTlMmNIbpVnmX4HUDdPE8A+sa4G/lFRwG2MAKod3", - "P4e2vlFIs/H0KeagE8qYpT/Jz+h57b6akaTF4+hFfuBdtKKqy14n2l/c9iIrKrI8jXQKrNAgJrPyuc3F", - "w8ioLaLCL+JLv30KZdgDclY3XC9+ig33XHTPRb9A8xdg/xfOeX6gMhPsP+kNvYQ9nnEq1OzhWNE8On4f", - "3tTVc8+sdip4lKas2GL1ygCyiLXQHBeUcZPzTR6Se0b4mTDCOKl1ONqqvLCj3aMCmbRMgwOnVYQSHMej", - "3p0jBT8fTp0ESt3Osl/TGz6j9gH1RelH7MOW67Z7RrzXGj/ru7emOYtu8k1BfykZgQYNzrKBMzwjlAgl", - "Z0z7CzXHG7TDPEdh9Rj3YuSBODuCC6dMa0cgSvB0eRejwls/xAWOsG5UCA0IzvFoqfnqvWjmcHiL9bud", - "lhSgjv1R8oRP/aRPQwP6JB0MNZWYteTumbirITpcRb2IclvXjBr4xx9lwAUqBNOhGpzgEsoyuQVBcm7N", - "hKIPVJ6p1GIEe4i8kVzC91WNamYsl7jud29fhtWB/IIC1RNVQh5xx/fd4hB5TJWd13XqQJ7Gufzhmwe7", - "vgVd4Er9wI1VevlcWr18eM2gPf4uesJqz73WsNcaPusaBw7No5v0dECgRWs7gVZABvQuHLqBzD5ro1YD", - "xtsZ5IXm0l6pi2z6cEyx8GOevejDCJut98xvz/w+67AnbgpBl6M5oxnTo6lSlulu3ZwSbAjrxcZkwbQ7", - "HpmxDXo3th1ZlheCWrb9FkDD8KFLFQQueJ2pyv+06y0gFIUbZSoFlB6p0gou2ab1hLbEt4UFsXzCsqzH", - "TJbOZiwbFdl00xzYihzQNHW8fSLYIbk4ewFTVc+9XXP5M9wFxv40HwPGwsmTlBZb/JzA8dFNKyjPHGiB", - "EYW+ne5MVM+4HE2UtSqPJIeG7wm2IvBfOt+htIYfHgJQ1gZ/yab23kPruN/pW3A1ve/gVhUR/quKewwc", - "12VqSRk3PdMZG0FOVdMHGX0hcjlbwcMOHChowXSnB++F+7Xhu7vjhnHwDrdaHLtyqN11aDBejFJjRgAg", - "wz9soRHwdQUHYf4BYVN4A4h3rENCKra42MFpjSY0vZ5pVcoNTnx1GzLTtJjz1CCbhyE2WFfijs4XcLAg", - "IdDH+fOuj6SpNO6Ec5XdrbppNr3yY7xyQ6xba35QC9hnLRq8JnQwfgt/npjvqWHf/RH9V8N3lxA99hA2", - "mrtaP96ClejhFHtvdeqh1IeWe4V+r9B/zgo9n0ml2Sil6XyLVEGKIJNlQZE9gpE3nXfKD9ec6U3vK5WK", - "jE1/Vy8tuN24nQV2cqeU4lw6Rscygh32zueXzP7EJi/5lKXLVLBL+6Dv3iYyeh/uH++3lwV7WfAFOg/F", - "iOGzycZiYDd30LrXQbKud1cNCExDKN5QycF4qtUHJlHdxlqbj6dmh032YMaWaovBXSk1DxgJZNoD92LB", - "q1323HfPfT/z2ul6OZJ2PgK3o/W9vgBvJEPzQvgkL0zf0M70o/cNhnVU1xlbhGvpCoYFtlbI2QMFGNHb", - "TrPdK3rL8zL3nlq1+a4Z6h15aqW3Xba69oCVzW7LeB2Cs83CojLzbnGs+3vDpVXFS0UzLmcPKaaqQfuJ", - "qEbzvXjai6cv8XJQ08AXHmjlONLj6M7Ncfvypb3mvGdNe9ZUbNG+vhDutPJAGElmvyCUBN+TDN4Jef1M", - "+Mmktmw/Zg6SQfsls28OSyy9FM/76X/ExdcQoxJZMGYE/bRygToOyGRP4KwEiETKS3XEh9DgBAvVVD+Z", - "zLJhP4Ok8eGnOZNnaiFnmmYOfErzGZfVH+7nU62MeRO+d7yu/mA1T23043rPUho6Ze903wSsa3f3DjPC", - "2tUdAWqq7p/MGf2rYDOHwnK2E4gaqVPWZWM81VcTRK77pwmiBZsUfSHVTjGzjkpxCKHvke/76fDCuc37", - "UtkV1TNmT1LLb6hl+OlBNH4cakhbI2/R+Tv67LX+vdb/ZWn9UUL4jOpGuO1scqfBFr+FF01nQedqzZtv", - "KJ6DCmUegZmm9bD9OGmrw56N7tnoF8lGG1Sw56GfDg/VjFoWvlXSstuHZaaR8Xty1WjPPXvds9fPPWBS", - "GTZScpQxS9P5tkQmErNkGeJ7ZjW4MzYpZzO3xsBbcUhm+qfx3kCMXenAbpcjdPoeCW5sr8gsOCzsRFwn", - "4PapklM+2xQDiJMZpm/6JH4J86yMDWGI+IuXL593updS8humDRUjyexC6esRRoOO0Bi5vdYctjNkpql0", - "HKsakPgBCQ6IkA2t7ZzlhokbZoBA+iRzu8vbRVOqPcLVoDHuLlJsfzm4i/TqE0royMnrYEDXAO4shE/U", - "I3RRcICAZ6SbVL3A3yue+1tEn+wl+qeX/4VOBBtNmGOumELOYZBWohurv3eN8eHFt8VsADBURg7mjGbC", - "SSolxfKwOylCWprNpCPZokk+0GNTlgU9snSyOcPAJAzYrB/ijfzrlNqdbCDuwPkTl5laNDw3z84vurwb", - "5zzLmNyZd2C3zii1aNC+X9atrzCODBBffhplTndwCI2Ij6i+JdlitIDJN52KO2Zs1S/Vwyd5V49lJfDn", - "srzjuXwKmeKSQYdrst975ZO8gVIQN0Z3D26B/l1hLX4hGNPSdP6WTk4IB5WcSygBBLn+cnpbf5iWQuCB", - "/VaB5Uh9Z3i9ejxLSRaboJ+S2dF1r23upG3uNcG9JvjbmM6j9PuZp+yLkFsfNvymYDJwpYfkvqoxbj+m", - "2+6x57V7u/QXybuaZNCRpUsyEQVP6EagCXEjgfPwx5Kg+xfJ39mLpCrT+fMbJm3clxp+R49pUsypYZ+M", - "r6B1K4doeQcO2KbMwp+v1A0Lf59SmbLeXoXtrOvrJvueSdc/GTAKLq89H4YCKUh8ownV7lNp1Wii1HVO", - "9XX4bMoJRkc75JUlFc1vKnf80NqqAujVM3/XNufuyHxyn2RwzZYLpRt/jZqD9Dm09TwQ8Xx2Ip4L4pM5", - "K8xYAWVsLAcE7wWdhiFhPe2mF2SLqD3hEwoS0Dl1OFYZPZC1Vn/XJo9eUFPSKMGea600MM91wJ00b6yu", - "MWGutdPvSpnScja3pC72RNhtyqBriE55nnML7tpQInqhSMaN5TK1oNIYVeqUGbLgdk4yPp0y7UDk1EFi", - "5rRgZkjeltLynA39/CcX56eO9WTkwH8zxBU5hmQOnZaUlW5MIMcEKkElTm01CWhAxtL0emQ1TVk9drXs", - "q7lWC0kOqr1VvzSHxjEFlywhqRJlLhO/lVGpRWSeF5yJzMtHR4wpnQgW1E7sCToWBS0gqvZSy2ZKL5tq", - "lN9/9IQzf7PpY4RbxQK4FTklBxbWcxToeYk9INWkA6GN1eO+0k5b8LZPd9JTBx0sJx56kawE+EBaUcFz", - "bs0wam62kVcKWApxB2sszQt37Xgn+S3JeaqVYaly6lQ/LT1UNVkB+QhQLgL4FT3GmjBqUp9gBdhNKk30", - "THY1r0LPYBP4Nfn3aj0/PYtA70SIitar6xhJFdMp6oG4VzMkF+g2AS9JeCPCjXlq7yJcB3puWQ5zr2up", - "+AXVmi7xnuToK5a0wn1PMCFreLTFBm4tGg29NT8C9ALq7r3wFa4wjGIISv3oJSBA0XVC0ZAQKhZ0acj7", - "AWDQ+8G9oLgGvHii8Jdcst8eUDWDXF/hu7cvw2OGX9mUCy9A7VyzRXuND7CwVlquwKj7MkwqxKXrBfjq", - "aGs9zLXMqTzSjGbA6VFCeVFkWhaGgCQLVYqM+KT3YGp4oapfUcQdHKKQS2CAKdfG1vaYBoFST6I4RESU", - "Jf4RhE35LQgrXF/OjKEzlhB4gHo/eBd6Ahs6JhOl8vcDJ/obvx1wCQUYuWGHxF0mfOPb+kI4LSUYLN4P", - "Wo9IXTyzbcAMrPHnNeb4Us16Ky1Czfztr9IahJolFXy5nKr604JqmRBm0+Hh8DeQxGFjezm8VQ7HS4s+", - "sBRuncfvSwbvJEo3iKpOJduNkZAqD69W5WxOSjnlAkuwArtFK/SQjIGPjOEVVZVYg4i0VCYkQkO4NJbR", - "7BmhQhC4p5BViWmcqsyoJk5GDcklQyuoKVgKty3ggaUQxOFElLE8Em9/AYx39XjWT2e4ldUFi8F2ltfC", - "os7bLXK48LQIRFcbbAJLzJXk1t3goHiuEA6qR0F64vEMyYr5Gc1wCaYqw/tNHWJPWKFScBNYzHk6R1EN", - "K1FpWurKF6GN+N0VMd0pr5bDhBui111wMXH9pzsPtBu1Owl0QpxC4fQJwmg6byYQiM0j6c3IsF8iOd2U", - "VBYtCWJJuEw1o8Zd6xvgMuyXksk0qGQJNnPrAvs9LsCqwhuAGz2jQOjBPe9grQ4U5k3C9atDFB4bTMsB", - "N9dsy+TAWFCOqJMGprFPEzYKPmGHm2YMcqEHZeOjAxaZBPecuBqqmWA31MkthQ9cgMrP0AnNNXCQaZyJ", - "owX4DUknCValuq33bPaktZUpNA6rCdj2lmsU3CC+mqrAbo+cF1rdMEkdkubMUtAO/MktHTYjoXt7iCbM", - "G3kqyl/XmlhcU7vwQxw5ts6nPPWcQzry945QXbJpDOBtcq/KRAWgjiPONY/5BKOqEjY0hHey8bEXbKR6", - "bboIfkNejKFVq2auQzK+ZloyMaIFHx+TH+EDObk4JxhqQA4cn9E3/kURvzyqk7uElZMxu7VMOkQYH9ep", - "3P16qt+GZCxUSsWo0CplxoyPiVkay3LivyC6lNKdGBVKzrxVsl5uy7iYZgUYp8P63U9hooHjrY2Joppu", - "QJVuZIsoKdvwIUgzRAbHrZAOnng6eYKi4vysdd6BFlZoCw5/A8X8YG3xAxSeMt2bsLpcI5gfrq4ufMkq", - "Q3JauNNdUJ2BG9kR95jiVu9YmyotQVMu/+Cz1PwNzc7w0LosvPzwWh6ZlJbkdEkmjFC5hIdtUJFaWs/a", - "Zs6lZZoC0z4VPL3eelkq4cbkmgZNwvsSkhtOayTEnBtYVKDX7YjXC7nvDSm6p/09qfOe1AD9CE72EW9L", - "3WfzwHcmwwRLrYoUAzy9vCThV1JQOw82dti7468CFK0OlWIWMeRcvXpJLJ2hRPI2qpXR3IGVRcF0Sk2Q", - "Wt+/u7p68zohJwk5O/9bhw4TVeb/xqG6HliLkPtJ2zFxQqzmed5hDLyNjc0WhdKW3B7VLsytwd1eoKgX", - "ZiGOItlyw8DLuw+8goe3AzdTUp82ntDGa1IDBX9ky60M75otJ4rq7FNgd2E/e2bXi9lds+XHYXWtc3lg", - "Ruc2sQbAH9nSvzNX2uePHo8RtsiAnrslJuR7ml6bgqbu1h7nQnfgpoHvgX1+TjMM/qmd4q7ZMtQCNKaD", - "O/Xntj6yaBO3PX998e4qIVfP/3518vZ5N89dVQfZPRjMZaqVEJfMWsGyrazGQGtisLlnOOHeRKe2blKF", - "mxirCkPSOZUzLmfJ75s9rUNjz6h6MSo89ZFHjI/DszoO64G5l2NPo9tY9BPg+e1RhenUeo8Xqm3jHdC1", - "mjHjkL6PWgLzLTvnWz70fN4ecwf+iXNtU0dVDHgvuKQiLLYJwqn1PqZhB4HV9NmJisGtNdXyQaZawWWP", - "IdXR+U37Ba1DeCNrfslvmFNDT9FU2cmRBb9h5IazReWQhR1qP3B3j5+WIvDurwz5iU3eXp1WNpzX7Fod", - "DskPvp2SYvkM3joDQ58qTapAW57TGTO9HxK9nfW+vDkGjj1L7mTJDitGDiuCv/wjcuLOo9nRSMv0UbDc", - "F3QJJTMd4o3X9jJuGJ9Xr9LdLwMvK0JZfx8YksuW8V4zP5Xxzo5QahjIK9i/J4IXEHfhiARcAb0RFZz/", - "qpCDcb2k8U7G8h4AP6sCH/pzhzpYovJi3IFFXFCOb1fRU5ks17b7W7CIFbDsuUQPLlGjxUdgFLEDenBe", - "0YgK6mQXWanBXj3KY8khqBBHqVDpNQntKgtQHbTEJcm5ELxxEDvVVt/ElZ5h6JN/tE6V1swUSmYQC9XF", - "FXd8kWuCYMPRvcJX9rMG9+jgOVc+vKsZ2KXCS4+jCqGMHZIr0BWtXga26R8EMq0gQKeUlovwuD+q+DEL", - "oWFmSM7lUaHVDPJUOUoGDw10hcckYCm+WDSzbxTU3b2BqVdd/AKM5e66yhC+4cEYXvzhBa96Ij0I2aJ4", - "JsCZZMZGgi5VacO1x39Z9wk/HPqsBlOu2TC6uyqCXzNjFXoz5kNyAkqqZtRams7BbkDJVDMz9zvOaTrn", - "0l8CTHSDTlhpZsqckQmb+rGJZLd29QE4pdodTF7YZfOn8HaFZ6MkQ4+Inrqg3+19uXwXJu7ZfCebD3jW", - "lP2PyOY3ntA2Pt/GIwzWitXKgCCuQKT1xuDxMQVmM3IURLVlWfXwXb0ih1+GzffilV7bIeRXtx0U55Lb", - "F5SLrUwzyIAUvGcdG5i4uzu3nAr+Adf7sSltZfF7OttKZ+7ARlMA2eOTWex4diMyY1nRjZIYu0qUrvHQ", - "+31ZVqDJHLfqbdf+CmFKPaUpG0KoZw/bNSxi+27fBlHZi5yiOkiLtjQ7YuC3xc28slyz2zktjUU/E1Ff", - "BtHWZp1cNEPyWpFpqVG1WFVmFk6VQEWFQGQ7N4G2fwsSjkFtT8db6bg6+I9GzJ0H9Shis4XYboul00rr", - "XXs6cAIU6cBheCAAsmCaEXjJKovKDciUkPB0Wgqx9CpsyMDQJsim5I3M+IDC9y2795VlZVcRlkFXdZDn", - "yAiCBTUrKzjMaAF+UXgPOl27uIDnLijwK1p5sDxZTdNrN5pXVfw9wBtzuCGF4tL+pnxmz2N25jEflb3c", - "h7UEWu1rPHHgWzWTEEuvGVBZNV7jHaZNSn3gu8YbYovcDp+6vlmnQbVgmquMp41iZsEqFN7Gb7zzUD8K", - "rMd5ICJc2cSeBrfS4MYjeGASjJ3ObhRYyIinCdbtPGIyVRnLyMXr/90TQSuwTZaWbdXSCznbtMfXKKHO", - "M8G2epAEacaz4OG+4j9CybdPn+aG/FJyZj3d4duDVITLo6mAxLfgquyDFHpaovzU96W3FX+BPYWtU1jT", - "UvqItOXxzldT33g1XEdAgb3CLdYn+jifehUZo18gw5XQjGZLBx+Pe+Ah5jRHNMu6O7BUpNBcaTIOe/dD", - "jDFvdeNFndvDhIxLDWlfQ/yY+7sK+xpjbNpYMx9t7gAwbqTWeEbGEWSEiMWCandbF0tSqKIUdS54aklK", - "DeubleOBiKXziPbyaSv1eAx9/Fvo5kN6YH+qFBJ8bTuzJgGGHqshoOCO1HIbXD86CNcdxV3UX4eQNgjp", - "bfzmTVqS2ePj52/fjk7fvH79/PTq/M3r0dvnL95dPj+L+6D6RXcGKIZNNaIHqySHtqoUQsECtcJGOh/5", - "3KwNLhGf2O90+NY3vVoWrGEOgBnWwqObET8+MvpHqRYy5JbiMhVlxsiZD0dNyAtm03lC/v7D24RgJqWE", - "XNqlYGbO3N0WqgQn5BXLOE3IC+X6XLFbe+VutglpUHdCfmKTS5Veu26vqORTWOGFZlOc442dM41sMlea", - "bTc0Ns6mhRVJjZAb/bI8CN/iML2lTDg+SPPREVT4+Oy3ueo9493KeP2hPT7HXTuXB+a1IVJ8a7qaKqQc", - "9AS0+IdQWA+NKO+ZN6IMd1l3M0JxPV2+B0uIRBy6mfyaHNl2srnz0GYIuYq4zHiK3HSB6k9p2nu6M88z", - "nrsVVBvHhwrMj4gMCRJBRMHFzUizjGuHDBsoh5taVJhmTl81hcSkOMJwSwGzSGinf9Shhvi0QzD4gusQ", - "gfC/n18l5OLN5VVcwBXK2FFgP/Ezm6hsCaLFjfLk4t1VdUlL3OboDeWCTgTrEGW4tTi+Yol/KiAmveHK", - "EHrBMdR5jRvABjDqkj2Q1E5IKfkvJWtmMmg88+wl9P0ldJU6t8XCaoazxhD6CW9TKGnYDtIbOxDNUgaJ", - "qf018YVbdMN0WTUE9HeH4t8MsFsC746AlSG6Gl8JfxtloAGFvTbQQxtAeH0MdWD1ZB5YH3DYGT0kfxIt", - "NK7ZKaSnm/rUb+TV+avnmNroo6oEfmVNnaCPrPMKjgqyY5M2k/O8i0dXmw4DVqBCwekg82Ruc5GE1Kiu", - "I1Qi2N8Vf/eSCFJs2VipOG9m9meNrUiqMtaRHRIadNgbomM1soK8+TEhr5UlL1Qps8O7Cky/k5oQN0rG", - "Czpjp5qa+QbLaUFn7CunksqMaaYrd7oU+5EDKsn7wckiIZeSFv+/94PgVHBIFnNMgFkbbUJnbg0TUweF", - "pZOkwglD8jZkZw9lIfwMfgVex0oasRbecc6dbZWaybjuY8TeYcjmMh6S0xB56tNthqWN3fBjEji2E9++", - "zmBfY6kb4L7SefUk9pK5UzKDl7XHjUeUytET2e3RbkNGsToHUJPHh0iDBuJ/3MRhkFKVztABRkl/lbLd", - "5L+dT3Vn/3Kr2HIAZyo/xewhLxXNerzvnL151eoQEqY6eLsBh1k1IowFqnzPBKkPRefRTe0JfjPBZyof", - "+UQy8DTy6LTffUoP/SSSFaMKbhFOgR5peUjKSNDBxterkSQ411DrM9qtkcDUwSMhmglq+Q0c8ao8Rpey", - "A3dPhVODbJiHQ/LOMDK2BrPULdruPZGopxX4t3e2VRN5CeE0fZNRYPBNRzKKrz1Y/CUdWBrEi9WuBJbp", - "GwZp5cJIcz4FO1VtOLzhpqTCQWfCBbfLIXlO03mrA3ruoZ3u6yM/q9u0/nhMZe+T0I+HtOO1Hpl/eGx2", - "OLI9w3eZl544W7h1cPry8tCjdhW2e8E0AECmjFzxnAkuGTm5OP+4Qmx1e3v51Q/3HMA+MuY9ytuSd7GM", - "VLVbCZttITSTVi/X/EIPfEGJpyBmWuyYFExDuuzDaJBtE6qjjFnKhdk9qjiQUwNwhFqr+aS0zGyhPNjS", - "Ou3NaTbSLHXqChTO3IzSLSD5rFMpy9DrAVJawiDhyQF85BLCblNRghsT9/zh9OVlHOVBXYgEIjfnNanS", - "wdgDt2B3VgdQgN9BInjIv7w8jIv+NZz01qYds2SHjFnwfV3cowWiKil39HbEY/XKo4dX03sMW7eHea/G", - "M61s2K+lDrjuoQSlxVZx8dJdo4wlXs2bloJcUO6uOS9PL36v8sLvay8ntsiJtHhs8dA8iQcWCyIt7siG", - "PU7XKI0YfV827JNTRbkPz+rhA/2/PL2oE5PyaXgE6UzUP4ozG3fzwhiI9XF7ZY+QKutmmWdvXhHXIMI1", - "G/PEbdRoyOlY9lv4se/Cn3mBDdlzjvBJwieKqkLDrnjO5ezoRAi1OMIn/Hi2DP6BdaeRpZrRjgVhni5i", - "filpWx7UY29zf2mOCC66bgtEaXLDM6bCTx1Z7x9X6DWX5niYN8M9vNyDiWLK2Z2F3nZJp+j2W359c181", - "5InQ/bcw4VVr34uzLeJM0Ue/aLfO4ndunAMds0bnT8U0V5dS7kexzUoxmFhjjX6BX/hxHf2SU6o1Z1BD", - "pUo6M8Wao1wC15pAyQFIXDMy7Bdfhi6UN2la4lYL+3xc7rACrT2P2Mwj6sN6ZE4RO5fdXvTuJtVlwHJs", - "sWvVp9dsQTZXfiLUGD6TPsQISGJL8aeCaqcWd+/nAhqsbwkqvvga4s1yR898cBKuIFL4yXQk7t61qtOD", - "1W76uC+rNQ5Y9WD1k9ArsqF51VjUmxQ2v7eE0tfwENzxEFeVj1oxsJM5vWFkouwc5VzlR2TauNN6cqle", - "oLkhjeHxJQbKyYD/MDmXGSucNoyFJZoxh88IJYbLmWBVYrfgG1UlWpuArOT2Y/p47J9pdpUHH+mp5opO", - "3hRMbnh0lGxRKTiWTtzl0PMTcJSAzqjbIDsYhtjQK4VfAO4DXmM/c4huxCa4stNWKjBu6uhSn9DZLSGU", - "MDSqlXN1WySp15faMaQNxamiCkA/p1fG4kuH5FRJU+ZMu3sohs+u6GlQAyzUfZpDyiWrGbWEW6erUbDk", - "cyp2ikV9KK2sfcp7pWwzEVo6GSFef1Tiu4NOBquMa05XXR5WjoYh2MmTLhCDkgyjVORyVyUj7s4V5J1k", - "C7GspqKTR9E8LLciYv7BqCjheY9rU2mlwFDii4mqMWGohumse4wH8wIT1DocPin4KRWik0M7poNHmlMJ", - "Jsim3+nfXrUSwmaa3wRlwzdJyJzKrBFnjDUEj9CeeUQL7tNiH0P2Gg3sT/ApS5epYAnUevdlC0Ed8rd3", - "XIyvc1UljHMtGtW9p3zm34eG5GrODBg8Sa6MFUtSeAAccZmVaZVxr9AKyssbesMSohlUXPfVVQ5bm6Uz", - "SBybohLel+n6We/NeCPHt2e93azXg2tECz5yKP2YzLfraHbPyg3E10rJvbaRKh83eRWKsSoplseEVhiO", - "NJwGM5By90x//fAXDtB8LFjHoTA/GacqY2N/wmAN8r8pScbV1DGkv2sWcOQSutcjjpsPJ4nJjFeQ5jvD", - "KuNfgRap/X1I0pyF/YRs/O47cJyvluBLy14gq3l+y1Kn/UHuzbeBRY13jz9xBxqJP6me39YZY2id8ywT", - "bEEfM8piUxREC96NWIie+cAutLpdPtda6Q1GTirdlbRwTY8EXTrAYLgDURNfFLedqGJIWndo94sTAoow", - "TGI+V8YewXh40k5NDtN8e3vr81OgU/ZcmUbsUaAUiGL4+xHW6T2CXRw9xyL2GAUyJC/V4uhGiTJnVapv", - "DjM5WGQIbHJuTbBcg9/NUTO+wn12gj+8mHKfUtK3MSh7Q7YcL4i8joDhlMZdBfJjhF2AGlaLhZAMhB8U", - "5KijM5xiFQZIlfB1iHhVE9hzBG7gyiSJKuwRl0SzKZf4bBYituhipYp1W0iSjE1pKeyR269wfELOiOEz", - "ScVHTpqzgoZ7adktLR2kRkAgjyknI+fx0K9MKmPRuuIVk0Eu4Nodb6J33Q6UbNZY7OQqba7VwZuq4s45", - "h56QUgFsdUDNzOHEMcmYsRCWqOQILGxQFUGrG54xPZoIml4Lbmzr21JqRtO5o/vGt5phacDEhzqOrDDw", - "YKVKiPR3B1/KKp9AAmlRNXddRiBQ27+WBXKfeojqm0yakWdGjW/bOYCH5KQNUeQ44CfQqN2Qqpl0JAIZ", - "HHwsGzWklNdSLZqBa62xHLuiixHw4FYt8wg0kQ+tgbP5dQOeza8DQJ2YXIMoNmyDdADxop0wdTe3FaA2", - "v2pAtfn1SmrlZOBhs06+yeD2yAHi6IZqUH0cRM5qiHxfAeTCb/H7FjzCt+9a4Ajfvq2hgU4pV8JcVbsA", - "knrX2uvbChRvHSTaP77z26tHCN+cSfOigkL48rSZ7TlMBwT8LkBjt/Bmn8SuCs8FRQNGiit/HtsiVQap", - "u+J/YNlmfQIRdyVCHCZNwSupQnlQPLDIaZaQdE41TS3ThqjSQh3/f9KjD0+P/jL6GW+w7noLRDJKiLvJ", - "K2PJd39sdEQCyentSyZndj44/u6PyaCg1jLt1v//VuP9r/947LDrTaHkDxZoveUkd9Koq3ygKAUaEuDg", - "26ffHO6qbAMGVUvY6EHwlhXCO/bF8rqARxPk/9U8h6LbGSmlv3OxW5aitqlKW5SWcMtyUBgaHbBwZU6L", - "gmOeVGDNW2SyWxXEmYelhWLmW3tdsdvgqAhvqSjLl69p7ua03iCnJOuvIKwP27tbaw+rYG/9uJtd1WmD", - "o8l3f4R3780JY1eSJzQIEg7iSdTeuVpPNo/b+VfVONTg6vmSeqEblDgHC5+kaEf7RkhtZJXHRea5HN7l", - "wvPy2+cXL9dvCVEu+5/0hmLSA8IcJw1ZOqm7ZBknwKQlr5068C+454BHz3t5VT2qTzi8aprQ3tdmcss5", - "uTivbI/slluTwNdudYSb99Lnmfd8xn9NLNOB8EIaXojvhXYo1Ibkol4cCOT3EhLa8xsWnoGOZkJNqCAU", - "EucrbY5JKpTBa14IizPhokwEtUwfpUyI9xIdGNDPkrwoJSbkz1gqKN7dDSkNq1+PwxRQkjbxGSa4nL2X", - "7mcYtD13a4LvNU0Z1DXNS2H5UZhHaTK+oXr8XkJi/hyvicxSPGyu/bvyUfUzbF0rcTQVakEMy6m0PDVD", - "HIdw+V42nCGYqZL+49j+AM0zMg17xqT/ruERPlILlVJhSKbeS6lsAzrjoTsHzPvqztWp8c/IOPw+LLSy", - "yhHE0KpLoKaDwzHJ6fK9ZLfwzof1byXTHgUd1brzE5yaIXnJbnlKhbc8ucMOsyCWvJeCy+twCfeFIoIy", - "0jg6FOkUHguwJrpGX60KB4fv5UnoKPysdQEkTQSjN8ybGGAET3xXZ/941jzZ9zKlDkiNzoRbqPNpedpw", - "S+G5U8/DplAXfAYYli0lzXn6Xo6xzcHheEie30IVeLcXn9LEdUSl35ZaOoiUVuVuFirEckjeeGFl3ktV", - "IKd5hnwjp0tHoVmZuhFqyyYYP8dODRouNLfsYDgcHo6J0u8lfuvuWMDO8ZcElpsqaZRgXiE0QNmpyifg", - "5Avmopylcyq5yZ3y9Arr7WGRt1ptw9MIadLI1Zxhhu2Qzwru/PgU/Wfyin+fvJdAz57hIASNO3xIfBUS", - "kmeU5UpWWpW6Can+4fuvDA7mmBI0OAInP5ir8h8PnCloYB5bOpQu5vULsOR08POrwOtalf7cuIn3tQw1", - "9uA90O9TVqx61TLZNH8gqxwZlrbW8d3TZM32e8vzMm9oN6FgQrCIkDPs7cD/3dMhar2uz+D4D0+fOgEo", - "8dPXW9U2kEJbZaOBpe4oGkNewqYURDSvdhYRil2aoCcUorxKCGmVvDc+0pMnnMpaUc3yjChvZVXaozdg", - "tlShK0bVYfdBMgA1sqc1ram/1i7/VGu6dJ+DN1QfKxu7taQOMahXFipgOoG7anarFHk0vTWkHUadJ0+f", - "Pj1y+yEpLQi43Gq6DKNPyukUoDlhdsEcIgeYmSgS930YWUfd5ntI1H6Hlrp1CyHcRHJmDPh9NXX+yk4Q", - "ofZCxO9x787Pvgk3uGUAErulqUXJezRXAoAHyBqUJUgOGrwnnYD0S1gtEu3YNGoU4PvNy7xRN/qZu6rK", - "mZMPKCCp9AwSOEtC2G0heMptqPfTBiIYplSgIqeAdbK6QoxqpW2zVReVvHCjBdLxwEMrb637AVHV6Uff", - "y4Omxc2qVAkoSFsW+EylnKJYL/aJw9tyNreE3aYM2hw6vTXUGw1yQNAPHD35NAoN4LYwTkNT8GtsQaCB", - "psbS9DpiwXBfQ7Gk3pjk6zxFkH3O/AMBa/I0p8E1SkNFVrfqzOknqJF2CzNuXgR3u7M59JMs4rsBKgX5", - "K6n1CyftMidi/xqUiKFQsydcTtWTjE3KGTRgWjcaLKiWT5CKazMljAaWgAyNX9gt+gwSUqNtvQ1Cw76X", - "wbDrZLXbGnQvmb7h6dacZ+i1kYFpnKcMblJglGC3BeiKTsU7hwNCiWWsKox/kMVXSKWP6kTKZl7aTC3k", - "IckUqI1BAWw44f3Pf/03amT1LHiDQxUEqVQEVnI04zfsqCx8jXq8IWWq71MVeqrc96UqAs39a1Xna5VH", - "po+QuqzrXO7g2AFWgJZnx8o2ar+O57fcIp90CItPp9U9CS+cld5WyoxpAdK57Smlq8qZnqjhbgl0EXwh", - "HEGiOLNLtG5UblikmFN/rfX2C58ngHCJqvwBaIWV8f4QA4rPz/xNEB9tYlQEI8cqufeYekjGQLRlMSY5", - "o9KElzPYeAZCGb2AOaTvhJrf4BtCyZxRYefL8N6NKsyQjP3nMCAlhWY3XJVGLKs+rRnazGs8ozdsFF9Q", - "OImqLqPPAOdvR6EUJJyyRbXVaneWz5y6HcqjdiEKlkmd8maqiXCsaMcyyt1mG7UOTeWoX9ESgtOJVITD", - "IBn4HUWZWhF1dDk/W8u4hyAYkpNJnUo8Bhs3GSmL9dqxUTChs5JQ0nWtKjlSNJ5dnJ91pBP1AJQ0j78O", - "zzTNvUWlvY0AT+8hlHpNdZyQcV5ay7T7a82PZ9ynYm9zTYmnik2sCATNG5X/yDudN52C+JLL8ta7FpE3", - "b14dXXMhoMhubZmss4dKeDxyO/7bqyFByZHhM/f4ScZunlznZjYOHvYOzaisyQGGXvHzDEIjZ7nSy+pA", - "MTgl3LZ8tGeVC9GUEz9mME4FdmfKwgHK9E8i+kASeQ3ce4HcLZABWCOl8pFDiccUyPFj2V0eu3WuiOP2", - "JipxHDO7GKspj1HgT/M2LbCUZ2juC6Q4JGOpJAviwhv616jlGRnnLE8bYimdaVUWoaU37lFD5tw+I+O0", - "KA2zY/IE+im9HBVK8HSJoSqv3706eYJfHGWa3zAJtFuzZyX9kg1RIgu+XN8On/po64xjzW/mZgX9weoy", - "xTTAY6Vy2NrxmAguV3wv3GYhr3KeOtmC68Qv6lVGqTVn+WiqGRtdT9YB/UIzRrybuAcJl+RH/j3x7jTN", - "1BtucQnJmIbaA5X/5diNfvw6eL16UzjC4StDXrH86FxOFcnKvBiSE2NKMN+TP8I86PLDP7AhOQueKyE/", - "t2apoDwHc0fqFBAD2hclJqdCeHcVf0UXVM8YnNrIKkvF6HoCbwvEWIej7vgR4rhZd+RuKlD8yJzqDN4s", - "DBSf9qfp2UhAwubZUay1AiurNmh8rdimhaBB7s2lRRiX++XeR/Ea4GnI25NXiEX3OI7HgcI2zccLw6D4", - "xMfAHzsUkVOV5/HRCG04QrXF7UFOb8nX34Jrh0kasqLVrMN52Zjokb5lBu4FxDCLwia+Kn/MB6aEdVOp", - "5JE2BmM48C/Qbec5y93HwyG58h6moArOlwZeqDzWNNVDh+alAeUujkRxAFtVjCw11yaGpwWplQyw3eEu", - "jwyzR7BLP1Wumv6uiLEGYe+GxEjYFW2pharjK7cEvGGMiY+zfQ629A1I6cOpXNtTCpcBasm3kEwGnJMV", - "magSAoRRagGyA7LuYoJvSVC3TqBwenuOY3y7bpW3ms9mTI+2EYBv17iK9iFFFCZUZo6TjU8v3h2T106T", - "d/84gjiufKVq2RI597DG3gRWIRq87VEhFNaaqIyrjTJXft1WES5v1DUqzLVuPSRvptZfb/BN3ZBxcyVj", - "ctAYxhNRwz2J6UNIyZFSSTI+nTJd35d8pxSX6X92ML3hqeX5kLzqQ/9R8/dqdeIm7JDfVSyir0oGCLWb", - "NnZShZgHbwvInrSNqkAKrOlm/c/9PnxzGyVslAH9mW5biq5zpR6OZniI/kS3n2UjEHJTZGazgAuGQ7or", - "W/WkCM9EoXZKK/g4GXCIrx4kg4XS10wPksGEptdOtZXZyLcJV+OqiZlTzbL6M1SGi+qMYR8hQPAULxec", - "mVOIDrxTSJavZ1FHHYagDMOsBfchd4EIYYid1wZa2HS+u8f76l6WfifrxX1OcQZilLhhwW5CVGlTlTMs", - "9ePf13y+2cdah+AMMw7SdP4kYxZSfVf2veCb6RAKnd6dkgAWnBvOFmGdRmEU7mMt0vt7+MfdA6FmCVlQ", - "LRN06DyEVa0/xFVwtFqJe6wPB+hc3snMaSb+ruZDTgmdUS6NbQXk/s9//TdkGyjBBQxHxbfVhFwIulxo", - "qLXddiVIGuXtTUJSwYuJcgIYvcGSZlxtPajKcyqzUPQw+KBUx+jrS1qm6WNj2DtM5FtNFSyiB6ng6bVJ", - "yDVbZmohDWxUCcfHf02quKLHW1iznP6Tyt0gFM8ZYsKg2WPi9QXk+auILQCmmSQGS2yuJOt/eXqBQKoC", - "lh+TUQlhmjHm3gK5Flrernl0UMWLN6PEkyBue8WFHw7Jq3g4+DOiplMn/b23EdYghSg4gEtNMY95ekxz", - "lfGUTKhh3/3xKDiAOcborn9Nuh2SH/hsTjAacevq0Sr6eCv/vk4QgK8mCTFlOne6sCrtkZoe+VseGJ6w", - "ihg+Dh8FIzsa3R2D/XWDitKxot0E+2kTJ9CuHdTQppjHGSLJcLGu1SZPB25albC8ypANybkkzXKuxDCB", - "EhmireHEjtEHC11uuPH2qQMvNxdzBWYlHPyw9umsv3TI4C1Obi4/uUHnHf8CmFaqEWicVnmfRbe+k6vT", - "HxoFZ7tWY3xSKCqDIyScFhn/+9fxIcSREqmOVPGsvTj0A8YQ7OA26F/jrpSvGUaUJhk36DBUd73hFFeX", - "kKUqSV5iTfAMlhBcgsZuI2M3whgOfxx3femFZFnRltS7odllU0EEO1eaFSMvSKt3PPfDGbu5UkoYrxGh", - "sSeiRWKmepaNvJ9qJBoef3AHCpjhyC8o6+3pMYhqSN6Ee7jgxlYH2zhVyQ7RfQ5YELtheklMWXh7FK5k", - "SJ67pVVJlxp05NXkkKZAkrCJoFK4Dmjt1ExAITufqimw8FKiO1iWEO4uR3khKt9feBH09WTfGXj5tQrc", - "ADFCkM/mzNgQtO3BBmrT+FwWpR1m3BTUpvNXqvQ1Q93tUuPD4rzMqYRArrTUtZu8wy1IvulJ67Jiy2Nv", - "maWeVafUoEtK5Yhbahmc6dqk7Kh2d4MOoCiAEU9+3bGyF6LfBb2valYZ4adV2P7lOodrIF0442u2jOEe", - "rBjQrxkKH6ScYfaYCKfhLhjouVXmAiq854TxlReUSEh9/UlI0B78vexwSH7CmhBjv6JxUrtLNJil4zuO", - "YXoZcAxsE15ZAo9/Rqhc4rt78KV1G59O0SHcKcyN8Q78XScJUfUJ3KmTpn57mJCxaaAYJCMLCgw+8UTE", - "P3DHCXMgx6AENSQn9fb8oYVKTrhgvyuSCkY1siYbP2XczDhXklulG3UfIXtbnWIAtYFDfLq39RiOs8+Z", - "Zs+g5oZQC9N2/QcTGPhw0CbI2tI08ibst9c39VOnpeDXZMBuHYvbdaTn0CuM0pP47ixi1jUZ9LIMF0Gv", - "wpBLmjMy9sc7rgNsIJ2cXGK1/ap9QgpRmraBo0Gs63fC9oU+iK/Yk31W7Hw0q0LYHc1eA/syNLAWNe1G", - "Gi+UXlDML6am1fk3+JlVuGofrdVIE7CWFhXSNDSw6Jggc/CcnZRSYKQgQgBid5CTOh6XgIKCRn2Ek+va", - "mA+G9pTsURQR52BagloEMd2HkJjFixQ/CIa2+Yzd/jurEP2aDxKhWwPpnCbiGj7DmEnEucYMiGZN5A6o", - "4hW6Si2LEbqyYmdKf3P18mJ39rnWazc0cd2fgOHGgy9wvTvc+yJIxoF7OkSs9dr6qImbvY15b1qSfEh+", - "oKjiTqeOsg/CIi1dGsKlUxBuoEQ0Bulsxa3elBjCMoKEYc9D1MkuNOhzOsFzzsQn6gpudPWyQEP23hPG", - "+5ygH6jPG7J2FD7KJeoD3+FiWs02wqFzWhgfFmSptk9qm5L3lXniWIN0V5onPkvgE3dZEHRJnKL2rMpF", - "7QfkIcbVR8A4jKeW+5KXjceQlZXAE01zpOjbhrGs2BDrVcPSNUTrWg282t9TFaMAf8w6oG3zC/iToV+O", - "A7XTgjwQDOYKcPsPDXmZj6aCzgyejwPRdu+vsOdwhLEHqVPB02u4kfUNNl/RAktrY0X1YEiCv+KDLirZ", - "oAU34CTY1A6SARjPIUo+yyBDyYSm1+gk5yg6ek5gg+5IWHHlX86gjbfrNxPVqIWELC8DP0x0grkS2eia", - "LWOXf5Vhtg33s9ufaxtus8B5YNTGDbMjgKR+/pdlPkKzeisU8+tVSn8N2frA0sBz5gmrYP71Msy7/h56", - "u76Lv5NUgaGX1vWkEGKFwhCN6EjL9ZH+z11GWkHX24EbugNJ8d3kbjFGIYZnRaH3QrZ+lIFYx7TOrbHZ", - "tbIrcOfUPxKd1I8zd3ikD09NHnebyRyoIQXVoC1RZPX+iuiWEpzsUNf2LP69rEcpsLpSFScM8XHwgAIK", - "HPZ2QADF1TXwfQuqac58TpvnXr1Wsvode7ayz8D7dbgd+8ROcf9iIOXc8Yxtqsw6w/o1GWSazvp1P9N0", - "tto7VzesX+9X6oat9gavQMcmtnW+cA1/ZMtGX3w029bxElo1uzE7QjPZ1q7MnkLDZm/B2FaN8dI18ijc", - "8ERe94MPPgprGNaSw43zbcEbRx4BMTVBWYGmdbatnYeN/NyRqmUUyH7TNp2cuGK3tgJPLF4vSuWaUcvO", - "uAbjy/JuwjOPZlupNI0sjE5cQ3KgUvAAhV0mBCIl/vTtt4dV5D3Igj99+y0ocY0UU0+P/vTzv/+Q/PHX", - "/4gXVrHzSEjhxCjhuE29CNcQ7IOw9ZVJngz/13bHJzdTDJhnTDDLLqid3w2OW7YQFp7BNA+/8Cp37d1W", - "H3NyOl9LAF/n8A5JTqsdJSgSjMGLz5Oq6RPQOofkRBRzKsucaZ4Spcl8WcyZHJKf3F3G30KTlr13fTZu", - "/GzZKnrRow8nR/94evSXo5//r//oV3LwDLXbntfIlTrFYIDulufh5oDt6oqLHcUlIZZ7pKll24f0rYlr", - "7Qb+4QM5yDFBiSyFIHwKpteMWZaCh+lhdNIFz2L4ujobNNu4/ihoVwXc4+jzjit36PKVDo9KfTRAiLm7", - "TVPNXUs4cuaarBXeDikh/EKcHu/D+igmwLWKOPFCqFBVbR4L1dSq/CNPY2eyMYmEr8oAvvl1fu3VtQXb", - "raNczRBCbi15FZdncqXs/K9ofYSHGXjBCdZ4p9C7PUyoo2SAs4VCCHZOBKQPbGVV+frp01ZelW+jG7vP", - "JcZtYac7TJwRh4R58OyppuSftwlZ/ty8MRSUa1OdnZ1rVc7mPqmyW8QMHMFfOU3Sq6aEWiIYNZZ8QwrF", - "vSdgtdLVJTejLCof7G8AePWH1d1s/BHPsoXD7lwjHlT4tnkk+DUj37MPDuBpCXmzAzbDCS/oEjdCuDSW", - "UUiWKbhk1LtgFUp4yxXwbZgNbBBmVDA9MmwGmIbkwIoRENkox9RPfCZVuwxrKzVFo3lrS9/uSJdVXUhY", - "19oJnuMq1qlhK32u7bN9SX7afUuulgS4heuCGv0eXj7+BdhE9wLJK1we+bq11u25jDp1h8rK19fetjLw", - "JqvOc7wq1n6JD5ZJcMXVsWcCQadmYwLBJ41cguArWY+Nt1j4ck6Nd5F0v39V0Bn7KiFf+ax9X+Hl9Sv/", - "SPYVuaGaO3Hrb6Z5IdgxeT+gC8otPPIOZ8qqg6/m1hbm+MkThm2Gqcq/OnzmU6KRRnMoO3Nw+Oz9IJ7O", - "5lPLmxXBh3tlz+p0eF235D9oBqWw6PVrKIb6VcntfHABLO4AKyQdIhuBTC+xLDwYGYNJZqugiObGIoZi", - "yB/TPZj3ee452sfK6hNHEG97flcIRbNXpbC8oPgmv+oYVVmpI7klUMDXbRz5ljAk5rRMLb+hkHynktB9", - "AkteYLAsZA6wisAjnYeon8l7Az2Zq5w9wZiS+lXBPHlfPn36h9T1h7/YyhXm5OgfmId5ODr6+d9fJ998", - "+238svyBFyMwvq8t8R+8IFSn85BdlHLpH4xLWdD0mjXyb9WrPvC0Tr59Sl7x75+htSyEt+ZU8ikzdvgv", - "o+RhM2Z8wiXVy60X12q5PjYldvQNfehpU+f5OuLe1By7gQixYV9wwc7lVK0fMTejjOvNeA4aETyrV/aH", - "uOqSq2wULyP/0imHOai4PuIsFJquoJhRy46gdywsOirJ3LbQHDPh1idQSsj7QaYXt/rI/fd+4K7Y7wdH", - "enGkj9x/7wfx8KY4rn9PDWvlyICqWeAksA6J3maccAtaZzv8AxtNlpZF6PnSR0fBz0PyFK4rYRmcmR6B", - "USHIjcJNsTFZEvCgcYYe6F3ohBFwHTk5XtTlzPCxvA6N2h39KLwBs6w/Ht71LKup7nqou2FJ3I7rU1Ys", - "C9Y02p6+fX5y9XyQDH56ew7/nj1/+Rz+ePv89cmr572znXWqwD9KtZBrjijx8z2LZ1uvapxWfgDe2Zd5", - "5wqvCWBtAExjWUdH0yq/AhXE0lslVQ6us34YzO3UdM1DX14fzTrOqKXo36d0DsJOyeqsQSt1S5kwoRbk", - "AJ9kcEn4VuM9h8bdcBgnRLMZ1Rl4vYB/jCJFOREc0uZwOySnVAimj+ovPQDAgejN5RV5Uq3+if8pJH2p", - "MmwEjwkohABZMQxjZLyylsrC8f+x9/7NbdxI3vhbwbG+VZHuSEp2nNyuXakr27ITXSxbJcm7d1mmJHAG", - "JBENgVkAI5lOeev7Ip5X+LySp9DdwMyQM+SQkmJ77/JHSpYGvxuNRqP787mVRjA747kAlhWZRsKxBDoT", - "UqermVTSxgkOeekJsbN+YwNiaHjCB6t7HVx+ZJtbH/BS42Dsh9TAy2Tmu6mm4jKYlOsjf7HYy1AK7eDl", - "SiEbcKsKz32JdZWBV8tvpc61nWOJWF0Zht45urleFuJ4u5SFD6tl/fJ1LX4Uv401YLAucSVtqAC/rRCh", - "leUzPe1W+o2ehrKVgGB8kd9Qw3H5PbxONtUD74Nda/lZLJrqwCexSDHduTp8P6zRpvd7mbwRlzdS3HZc", - "5DfyRvxFitullS6r6bzeoabVRacY50pVG4d5gkWOKiWWa5NKRv6eTpUdK0lcN8tVReDcreo7C6U2VLp1", - "fat1VZPnulRV5lOEmqq89BvqIEaY4zQTy6W99pdq2m2aqJ43WKY+SaFCExxYXWoiv9VqHZgn2rUS/DrU", - "AiTTAQpzM3l3DbGVSqcaCKUAexs4z7tUdKTnFPbyBorUasTk0o4qwdf2Bgos6YJaVTM5cd0r8l/Xq0ny", - "LqWTvF5K806917ze6TLdtkPpt/Hjljq2mMeyrqa5rHCRb0vz3us3cOpuT1kcCdK6HKDLPKL9FejXrWF1", - "e/0VtLptgQDvi6lo+YDvyFS0alV0LNikhLYtWlU925Vt0KLbVVCq847llqWna7GGHblF0Wa1uEUFpS7Z", - "otDSXt2iZG1zbNPNZT27TdmgZbdvr6rUdlrQXWpoNqS3Lxzt5+2LNtjKHStpsai2K71qx25XfsU03LH4", - "DuqjxXjuWLp2dnUVuKZzr6t2X7rvblOscmfpXmz5ttOxZOO1a8uyOzbd6lfYoXzVy7Fd8UavS8cqGm2K", - "XYkCkKnwjbQOXM0NblkDJDaTBievVPjmABA6iDM47Jp+Hh9SGuJtok3TQAmR6ekyxBvP84weQ9Ym+iw9", - "pOhpfKl14kMzgWjEO254npVzfKwre1SlNur6ItMS/lBtusnHfMK9PfW5AjTn3FzfY3imr04AmzxPK2lu", - "rVGbW4Zqtj1jvK28YGAX+kg/xNGRe3IKlLe5E4ZB6MSwznn7mOIYwr8fbVrc1sfKpdXsFMTQ5YG5OkKc", - "RZHSUBvFXU8mVrjGYEFgbLbECwaf1aeu3I6V5UKypaWoqj6bC24he7GKSIZw/RBGAsBO5hpfNiF8hhdu", - "po10GPJE7Qd/Oy0RVnBrvGBBIN1EKqAe7ARL3vzAV05I47LpwopTygQ6i26Y5ZfhrilKIQFg99Sktho6", - "pyStZIJsSVV7f+GmkBpxx0DTVFrHVSJq0UffPXR4qe/zVuGld4+5pAfdMsDS/8iVW5rF5jfeTeJZxq8G", - "CWNO7ySmXWvaSlx3z69IhXWXm/JEKonQIdhgU5pFv2dNsqliJCvoXOdy0FNooF8ZRdMMvbuu6qUtouJ+", - "FAoO7nc/R97DVeNKX2+U2mOVettM2BDWNdwc0qWvG8dyyl0yoxyL3Va8LcniqD25IiqKx08Ot0+1OGpN", - "sRiy40lpBRWWMBIIb6pkRsIiJSE9iA/ZQBTS8P1h/9vD/uPv+o8Of23uIkwtvX1sWq8JhWAbMQGmZ0hw", - "lx8FquCIs4rkwcHkA4QJeAFHQIFmTUOZ8mW++Kr9WbaOx3kMLkOCn3L8IRzGaSaUtyaYdIynPMd8MSVu", - "A7tCGYcKMgFzORM8nRRZH9GXwm+yFvFszW05as1piWLz7ePDbhkuy3mUu528G7JPwqkbji2Eql5YTDlZ", - "OourIuqX+7CP33IjmAOI+c0B7msO0pgQON90ol6LBbJUMOsnh0707gdsc/shrNPXbhfzsc6gcWhoyF7x", - "ZMaAzNXOdJGlbCwYr3xbQbAbL9iHVDuts5Has0Kw/3r0CMaymPs7DNAPamX3h4yiuG0MhRz1ziC2d9Tr", - "s1EP3KH440tnMvzpeUa/ev3dqDccYRwjhvcDIzQkj/gO8sxq30uknUaydsqnxPr+zYUgPvgXtPZvF3wM", - "1XrrwvcbScb9jsgLlbgiWmn1agM4J9U1l6qwtdrzzP8CCMUz6YThGV0AQUn4rvIkEQAtBhBSSHYOmEdU", - "w2DUQ9QvMwczHu6F9faW4O42CMLSKQNS0XjOICDsqw8iubcEAx7BCu1Cef2ngCJu9UjlZlrPVflbA/Y0", - "1sTNtJiL5RyhjbuB20ujdQeG7rOizrkGSG++KMuNvJGZmIoWdcntZUEgauurhJu2BNZfuJGqIoNTL5xN", - "q+gYIexnJdgPJjogT9mZyCK8GJxhhWq88Sa3TXA82gAxXOnp2uPV4MR9qpHCvQiEUjUNYLOtKNRNu3j9", - "3pRkSGv2+6flBXulbqTRQMFfJo8A45dw0YRYZRcoJX8lAWS7nI/2BWxP7cDl3LgN75TXwaubLi5YSS3d", - "2+oe+yqOv+0S28zcID5Id9mcSHQauCsCdWcLESGkeVyOv3/SHJNbQXwmymAkHR+2p3l0rUxHBvNht1xo", - "GtLPsoR42BL8FolLQXpV9AlWpLe+ZIjWWVNqvYtXZye99fVWI4Pp85+P37zp9XvHby96/d5P7083BwRT", - "22uE+AxM6F1PE2QXYqcX/z0YY85F6zQkOmsQ2bfitmQuTnRWzJXdlPDX7xl9u6ku/8mWmYNQax87umbG", - "znN+q6oT1gl3vOHo/tRf9scRW4+4dG6x+RR8Tl8zznIrilQP4uj3Ti/+e39ZseKNBA6iGOV4I/BEajku", - "mxftGGirs5WFIxzAyiDAE7qcb7rFkq605D/bvZlVdfDryrruoM+PK69NfOwVEmfW17ZuPzRy7bw7j4vV", - "xvka2Iyaip8LcyPMgFu/70VacvqbpkM2ep6LQjbz3uMj5CV3zY9MyLe5woBLxbZ4Z2rdao67wm4LHFvB", - "/SwsnrLtWikvLvOkYXyvrJNzSH14efqeFfAYlwuTCOX4tHoKKkh83nCMlszXss7WNOOWuOO72ChIWdiS", - "PFT2OBDABf457H3MK2o5wRvdRKflmrpaskrJqozdbz6L2hc2lWq3Q+eIO+412a2R6LhdEj3MBJWAgL5q", - "PnHHOxkWabWVzaTHsd5fN475Tvai7w6hblhf3eoI6ZWpTUjKNH34IDxKDXtdXUE0FCN4mRi2je10/iry", - "/BmRG2G9hqqQvFMKrzYrXC93Xc34DFgKC9AENF59mh/539S7tJLB5bdCI/5KJ9UQFSlWLi0bQcFRr23L", - "+v43nALowKfMKV2hXk5mhbquo2ZCRnXM0+64iTH1Cdb/bn6IsU4X5NKBKgPkM06Aot29nA02XMuX3ZRq", - "V8KFR98e+CnSG2m1WTwlZP9rpW9D64TuBw/AcKwyPFaX4LBr778ZEvQg9oetYFoP2TEiKqtsgS/5vsFC", - "YYNJYZ2XzUUubN+LAfqMAXUVdUydejiQiJXET/1IQ1ehqSrZvCrkRzWqtEifU2MBillRZZ7DWtbxNu4E", - "nEfa7cM7U4xvyKSsGDub9XUrzBzGOgjTnEk9kQpS/rpYRGWwQSjVZg9tdC2hqbf6axsjMyp/r8HMdLbf", - "lkIjdu7s0jyDXVntZ9OclxGcZ2LaBcKz29PZT0ScEYJMpuQPWYNO1vKY8ld4RNmmoo6BFVjXN/5mlg8y", - "MfEHgVHiTqEWW9TZ+JodZqEfJnbTku3yKGTiQm/A4awLRuNpVEfr3PahPXP88sP6t6mftJEftQIsSGiL", - "8bkulBsyjLDxd2j4vWUA0dJnSkx57fd+HZoPcezBBmy2v/geJx3aT/Wtami+yJsbv0swScQL7e7f37Qr", - "uCOE9BLUtN7U9pti6yo7R3isIL1uqbVkmgq1AXwGI1HKZz4qtDFMgb5r6fZrmYlTYeYSQhbtbv2fGl3k", - "zT44+BOhMBj2Y82RsS3cRwME6/dPnuxvh7iqb1XTk4/vK/wJHnlCf9+39LcLNASiFOTl3OKLND5+EqPE", - "jmioa6A6qtDBW9IA88KKKhQU8gXmIvF7P43PCFu+Q1Qf8wEzuOkZogq6VYt7O9y4KauNN06IN2Fe279y", - "l9wrwG1EHwbPAACBN8Nm+Y0rb8RmF27c7VQfi2WzRYdwpNbgKpiBO0ZhA6F1c/DQWWnbho/8Ek9yv2Nv", - "hDEyBe4fuDbRDOxX1/zx4SZ/cKN3NNzdVvyacFVaisWmkGl/h8T4TllBBkNWqDI0nAmVEhrknnU671Mk", - "uT9QkVYWwXiRPZlnmb71peaA2wXw8SqQyMQ67b0BAVc8qltFl8/5h7AXj9U57r3259Oy6erzYQh/Xb+w", - "a9dyzj8AnJD8KI7VyYv2HkAiR6CIP3nRUZiWcVkftYTD+dE9L1KpN+/Ll0TJx/3niG1rZSrYjUyFHrIz", - "3IO26h3wJhK/ERAwAqUojtLLy2mRWfGcfptcC1clytnzlQBODgOuo7F2swpPzj5JC4aI1cPYpcUeDbRq", - "1RcNukHnd1UN2iTC17N5Jo/nc5FK7kS2YH5jRbrIqeGJmBQZs7PC+W1GKEFzCEoEhyewNyXamAKicWCo", - "ICPNj1V3SBvBLf/HoHr7tvJ7QfUu4YLUjch0vm0k7QWAJ2NRFh+NnPY2QAXpkC1BHTXQRwV36Vro/zrg", - "FNAq/L31xWEw10o7rWQSQ+sYPrWUPeWJ0dYSw+tEQNAHrTJuSiROheigN9y6AbQ8OD6i2NGC8qTOz18F", - "bykdENIiyDH63VZSNLZ4VPZjDP7kX9euYVte2RLSFqad3EojBpm4ERm52QAdqoxcIxQuWrl4uoE2Ckhd", - "hLVVjn7InpuxdIabAJhFljfSWhP6Vok15RVkipUN2WttIjjYZkiwfhOWF/RYmAG481BsWKoTCCUDok/k", - "+iX/4L8SSNbB0m+OoN5KeGOfrSKBNZKidHUify2u2HI1//P83dvoiW1aqkxamuL14GiIFYnvN8tLVye2", - "aVoUXFM/93d1BptC+eVofAN3QeDoZI7vKvgMBLQ5txwiBrASP4AJsfaC9ZHJuWzJSXENBtR7JT+wmBWJ", - "lx2vmpYAh8uJIksRFNZt5fTolA/2R7nC49qfh6fhHR7h2zh2V6NL8zyTLb7qv/IsGyTABxmy8MipU5nM", - "OlOzX1+qEhOyXAAZrxEYVol7u0cs9Innbmu+18jymhqwAS5x9zVRU8wLQlFX5YstRLmVgIM8AvqxuQSz", - "ZSwSuNH7m47IMjYWM0msU+hAsYU3x8LBGYqjeq9PILor/BWGGWn9hk50ASEFnJ7A6MSUlo0FveBCfjGb", - "cAuJqTOu6BkLPzCCp88AAlLwFKmssLbA0Tzj/lOhWKYt2Fu3fGEZPRL74wqODosUgETaLt0zxsfhA07f", - "+EIpd8FXCXu+T0JDy17lEP0ojB6uPeibMfJ3s1/IRMm4dSumFTvSAvsHzKeVlWpYm217vBLiCuKI42jU", - "AEtkTVv71u/GOXItFtYZfe2lsIEnoDHoq3mddkpjDHHKZT9CGmclndGfJx9EymCww5GqqXpTCLYXZGwe", - "ElgP0sAYsz9k58iPHPN/RooSNrwi922B8coV08H3UWmvNlNsD373w6GfF8qy3B+OVIW7Avj2/Kwtcjzr", - "b7VJBxYyGCA0gCLp48ilcoYP/FfYoB0prykURwBXsHDwz7nXOxZtU+wbnrO+L2uWrpGztd9CIOhFEeYV", - "GNDwSJ9pyDJB7r4WAF596TdMItbL4qkwg2TGvcXmldci10yq34g/23Annnkt6/i1QMsXrB0wKmHOxjy5", - "tjlPRCkE7HDI3qlsQQeRbZoBtmdlJpTLFrV5GqnyM5CNfZyq6PM4HD5qlPoQjdaVPPGvYnzy8vRY3WiE", - "qSBa2y23eohsCVaxLlyi5+KSAhwaTVYZ27xsed/uzMxBxBjrCDqWx7kTlAFAu0Waux1GhMFNTQakM4VF", - "/OupGATmRgqGCpDDFsHq/bELzkzuL6nOW3rc39LnPJOJ1IUNwW8U1Vc02BdAvSHV9NIWY3qroLOYM6XV", - "gBdOw58cS0WScYO2CDyfAZaIzr194k2OwsFxhV+7Oimv35hAYQYXGZUIJJpAiLx+Uy82h7TXZzkOcf2i", - "X+8IGwAz2V6m6RS58MobyvXxHoH3i0fsRL6gXFG4jVlhJM/kx2gPb855qZCelM7Pza50f5xcgpn6exUk", - "5NHjP20HEhLr6dO8tM+5n4Qtp5orpR0m8W2yq8s2nlcKLScdNe7Q3M8nVLNpVVdGFoBMVsOkO11ky05X", - "rrGVlVl+6uB/B34Vb/F723GiDfOif402rbRMfOCJw7RnI6bSOhMY/p2/COg5ZHPe8EzS2Sudje4UVnEB", - "hHhIeEQBqo+0w7WxFAZCJKkOYGmq4xytl5jndRHY6iGzqjbHYsZvpIbMTmAq82aJJc4aPL2LqHExXxHx", - "2YUCzzb4idBb1XBRjaqxOejKX6rA4PdqufkTfxG61CpbNP859i1g1zZ9tpy7EqtsKr/cq351FOvX5LUh", - "sd/25bAxQeEkXIrgxJIqMYLD5bLpRU/MxwJQl6C+Gtk33fmXJHhzlklhsnYeSGzm/dkbtOjg4mg4kpZV", - "HDTr90UcOza2fnLPo+bYdna7Jx4tL+WnlazdV/V5ptu11yjgbYAIXK2zvtcZQLsZHWoYYnYjMrBbqoXQ", - "fVYPFvwUwHqly9aQf8KfwyW53kJLYMZUXK5dWL+kjfVttdD9nuPjO0q1YI6P70WSb6VK9e2duxPaxeru", - "oWdL26HsZpy+mhRUlq9Pkr1+y9jOqDFLnPSaUtk6YQBWrJhNyfpYcWOnjXQiUunvZoCuvwnXcmACGU9o", - "cFdG/U9gKGH4M23VHj3dHKP/7fnpca/fuxHGYncOh4+Gh3C/yYXiuew97X07PBx+S1Q0MJCDAK1yMMn4", - "NATZJA1RNifCTAXApMCXuEfFB2nBnaeVsH1W5Cl3gi1V2gDOciM5s0UuDAT6p310YADxYKGczGDm4tdH", - "4gZkjI168CCipJqOeoCjmEklIIJ/DK8K3siYaBMY8OCJl1CE4KLu1xDjLlIwT1wyC628hvHjUgjrXuh0", - "gXfoeNZXYCMPfrNoy5ZG69JpEGZzSfWFIeEcOs3mMK3En/W3UW8wuJbaXiOCx2CQSus19WCaF6Per/u7", - "g1dgh5rFqvyOzoMA4ATtPD48bAgIhP7jeqOxFodGi73My/ep33uCNTXt8NjiwQse9iQyg37q977rUg7A", - "ixXPqBQwCc7n3Cz8RR7lMnYx44VKZrQIvvPU516/92EQXyIG5ctj+TroKy7lO9f+ci8275vCClNawiUD", - "HhDaGmkFg6oWrAytiXk2Yx7/DAx4/ZHauKHY9vtppLbdUC+FAQ7iMAtszhWfop1+Ta/KamJ4IJciOWeR", - "ZfFcOK89bH+kAKV/ACS1Io014jhi/UFQ4Zx8eXR6EKD8tNqH28I408m1SEcKAgbCXG7c+6dhGXff/t09", - "EV0Wf8h+DsBJ9CeA2RmpPYLnIV/mS62vpbA0j6MexsFVLlTwMIw14G+HI3UuBAsUsCDJouzJcKr1NBNR", - "sA8w+DSCi4XfU+YTwhP58b/gVibPCzd7dyPMT87lryClPQ1z0NhhMFX8x/Z9PjU8FTaWomP3hH94GV/j", - "7Smh7Peefvu43zvVeZHb51mmb0X6Wpv3xpsSf+s10Nv2fv10X5ovyMpXq/yWxc6P5S46EHlNB3VC1Fzb", - "JocqUqACOZxhc69XSg7QjyVzKHZIfHCGJ/AkOEde05HqSmw6ZO8g18AsSt7RCh0rxMgQCWvKZCUYzm/B", - "kXp5dBoj4mhevOoLc9gnRm83E9Iw41XsXITjxEC0jMXIDb97JgXoPqgF1CbqFIYzQ53BR3FEgCKbKger", - "Dn2T2JLyBmbJ04KQehA8MlKvKmSzeFUE0W7QMtbJLGsAzAsnhu9zOCL8UcNTqYS1W5lWuNJln9Zq1nmg", - "2T3w4jUIUTOlcl13J2il7O20wR81Zf7GiUTRFnXS3vQz7+YVFuHafmqQ9rvv6AFX6SDoh7vv7n7D1u5M", - "WdwfKStc3HRlC7j9pNrh+hFlf6T+wOvH8h55rtKzqIO/4t3SbzgOcYrjTIJPm6eLL2Erbdg9bI9bL712", - "v3pKxiF23lz0GH4wDteE5k30KuD/ee2egtMuI9zJUAXD6EWLgdhWqmkm/OUfWEeH7Dn9lQ4i3wVvEZd+", - "5mxBx9dMZ2lI7f+QZIWVN4J5CxpQFZWmdAx4M2BRdi1LuMIgg0zwGwFnT0hVsk7nNkQBTKSxjqjkeQh4", - "oqVhMuL8YvwPDopo74cjFbhpCwvh6P5QSmbEJp4KRB7yB2UZyAOgMghg7Vu7FgtwuITpGqlwoud84Wuh", - "0FBmdKHSgTMyZ/72oRLEPhAA6KlSeSPTgmdUTdNGfgF3CVqd5yFwdNebxNrQtdWWImzVjvYsVNnCpf85", - "d2fcCAx2TOMGqMp0+0YMMb71fQiMppcgLtXdWF9ZoF+CoKAHWtCygbuu4wkKPu6iuO8/6xKeSwiX9GuI", - "2xLmPPSxJVRo20VEr+qBP07a1/FM8PRlxQPbNJ33tZ7YCBn5uJxLDoDwDaMm4Sxc2Xl3nn4/aAwui/l1", - "Dc7oHecbfNztE153sj/Q5mn25O+6gcB7H9hAnC4n6cvRiX/Fh4UQcHcfC4oMIK3rGNPpH2gJV9L1u6/e", - "vbRfoTto2qmY6X8jAyN7dPp8MSLxk0wJwFjf1jldtpKD1PDp6mG4HEINCMwqRdCJoNTHhXNa9WMorDcv", - "gxuB+34Zh7GnkKaikNEDcjehv1N5I5Aqg+zrTHArwAAMNCWW8WgE/+1Dny1+rWJJ5FyaxvvVUQjofSDZ", - "jfXfVfP4ir6QIxu6UtLp4DJxRlgHW4nUVDiUqMucGI/a1cyPwtW4kR7yiG4mYWre/fDUjlMRB3Ef0/yj", - "cLXXfDKPUN2Elu7FQvK7bZOVG0mcHmijrJBE3c3GpWnyI/u8m+UkcBPVli+czBGuo9RV9l6WFAgnLq/F", - "YoOqDonwsSOQjwBquZK4EMFE8MmpRLWp0FyMVBN5BWYsAlFBbsRMKPQfrLJk9JkVYqR8Z5qZLhh35YvU", - "VLrhxAiRCnvtdD7UZnrwwf8vN9rpgw+PHuEPecalOsDKUjEZzvDIoOzCmVba2GoGCwUYhfFaVliCykho", - "KgAUxZI/EpdJp42Ph0S98kD7ZZnZZdftAgsK0vIlWSxoRlS9biCX97EzKim6rcrugl+L82oq74OYtStA", - "bJ9oEdceapAkd5AjcGDZUswmG8OLVkME0crZVXYAM+8+64pH2A5WLlCI/Lvreussa1eDiEHHbginDXFA", - "D7TXDgE7zv/OVQzRirKum7Q1j2mNgYhs1RoIHLpfpWKZngJEnJPJtWV7SjsCKCTImlLEYnw0YG3ccLN4", - "xlwB/s45JJVVYUchfQwgScqh4Nt/wKQDBDvyAlPcSb8Gm0rZT/AEU3MO78U6wF4vG9jHMC3wx2HeVMA5", - "CMr0KqTJoadnMDAiF9yxt2wwwPyzQ4YPOnhrwCedqyYdex6g4B5of1bACXfVryReX4izDTtTmiO4PNx5", - "8/0+LcqQ5t6iXik59YEWbjn39U7OHky4/GIORj82dO7caZkobb5dK5Y8auH1j/n/YWb+IkYWA/cU4ePU", - "E52lguM7pEoBtD3MxXCkTo2eyMwrT6WVmOduUQnmir+ih1mK8ogPiBbfsuPzoXU6P4BeIdjPSCHMdwnD", - "NGREqQdYS9hnoAwsTIa/oq4BWtcC8nIgvXekrmLLl5nm6SVk6ojp4occ0nsukzS/AtQd53ynMaXn5dFp", - "eHcHPAOvIJfiSigxvnxuAp4DPymNc6LN0rQM2asYsjGgkI1qEyr1vRipSjcmmB4KSakZ5Lz6jvupDM+V", - "vi8E1AKZz2HBIDP474UwCy8DfC4c8GqPVEh7HS+Yzrw9jEn+IVvfCIj8q6MqYVPhGBuOVJS0q7goVyyV", - "NqcThLOxsG4gJhNtaoEtGPRSlzr0DlEwSjm7lKyIcOV8KhiCqbzwS+E3kKU0oiop3FW4bFwRLTBXMH7H", - "FrpgqR4pf04rIdIhe+5YJri/06jwvIKRcP5zyPwcxzWPEGTLsA3W+YuFKfze8RchafF192lFEMo17sMc", - "gceMNgocxCRFIYwC1y5CCCF+wkg5w5UNV5qnTE4Yh2dNU0ZP+t6AzPhWucm8IVNqQQaQamIyEYkLuF9z", - "Dnse73CYxZ6IMggKklMff/hAb7250TmfehMKFILfTZg6pL3hYYWXNCfYVRmo8a9XCGV0QAO/grdsSs2O", - "aH4kYQNn5HQqvOk7UjizqLmiXqorrcb4z6BjXkZ92e/FHWAhfacl0KG6/4POIDHSgFnKrqijV4Ee0ltc", - "rFD+FkpBoiu7BeQ36iqv0umtuxSLIbuqqibSSxYVU1UbaMNEJqfSi2lj9JtKQVPYZlWBwONkakLOYO9p", - "D3REyCp82mvRnQCTFM7xklerDC2MPCXxN+WQmpKMf71TJMxSOjzEul5WY7aXApguXg/+RKBM9YBcNuc5", - "+7////9B5WnFnCsnE6AjPH1+8fInthoS3sweSF9dtuQHVHqAYars6vcRxu6Pek+r6QG/frrq2CE8VJp6", - "Q5utSzfmXmODhd/ssVhlWr5ie4BYfoB45QfCJcMAmojUoAGjYXVbI0oF6r3AJiwjchDpnWgllOB99Vjc", - "EqGTaa+u6J5HJG4NZCDVANa2zf9R5haeHELvIYUzKSARv7JXAbcYh1GCjawNrNuvsXLWRTckOVeRCZ8P", - "fuGDj4eDPw8vB7/+/qj/+LvvmnGWP8r80h8c3VwJ9bSRWJZ2flNC1TKeOJ1Tl3ROrc6m42b40bpwkFW1", - "HRoUFqb3qhYSCBkUBHVHByGpAjtkdMjE8GFEfAbqUaGckdVoeCoMVupBYOwMN2ErMl9+D3Qoxo+zK4pd", - "PziNed/2ah9BDK/8vOWX5Za4QjIRUKK43BRhFQYLwblE52u9aQEf3Bqe58KwSn9qOEJty0VsEs2Jlu/P", - "3sTHYjKuxJJpJTZYSsFQ6rMM8Aj8pko47jXHHh8++RMyFvXLrecXMIEMFjQVQUfQAmAvxploYZisz+Wa", - "q0uJ2RRmEJ4Ky7IIH2pkjsEPSzIZpWLPWy4RmJ/SJYFlVnzAHbkR8POLerCuW8yoL5+Vzv4oBRECpHb9", - "Hd7l/vvk8M+by/kOZjJZuTXfT/DNsk0Xbtmt8yTC1Ql1eUxUSlk+4zDF1Qv6c7wfw+WlvBuDS4zuzHWr", - "P88KuzL3+LrZKWa0cj7H5LKGLCY6dx/qMWL1aP+jZZ5aDzh9q8v5nqIyaMLqy/DZZPrOKTvNw+koPBN7", - "kBjBnbiMrNQgSEVTmCN8GHH0HyrWsd7KVsL0aB3sP47zC/Lk4UgZh3TptDKtXVcOUe07rNwRfPjQK4et", - "nHI3u3OoS1w0HGJ6t935ZHO5t9q91oVK7zFGBnrO+F1WNtjjaxb1NZrdX/Z6AjHMP8FS0h2n8yoS/4Tf", - "oZcfJQDuT4VrouRwhQH8tl+OT1m8tVRuO+ESEyHSS5qXIF7D1dA2av9Iml9kvsl1FdlwYo1oLTsdbyXe", - "iAmDavP5UBBYXUqqDp+NBDq/bmUk0Lze6R3cz3oYY2QaANGrTvDXKLm0WFU15O8tKGjh6r2rRFuXdhDp", - "cI/fc9xULvPzEHICNrWva3+t5I/UGtFnv1iXMj2ZCGOZlVMlJzLhgMxJgMahQbLFRyoV1V/5n7nB2+xH", - "mZPziCczKW58T8bCLdcCG605pLSy7/wcfS0br78CRFIZLsRFDdlPcjoTBv9lA2w0s3MAuSxdK+PCMcev", - "Bcu0mgozHKkBroR1T9k//GpjFexRnxEcol9YkbK9f3x7eDj47vCQnbw4sPu+ILmI6wW/7bMxz7hKvEnn", - "Sx7ACrC9fzz6rlIWF65e9N/7YT1Dke8OB3+qFVrp5qM+/DaWeHw4eBJLtKxIRVouoZoWx3f4qXR801QB", - "MF/4G3YZfrCu1QveXW/S7r2T4rxY8tH9D1GeS67JLRQouJcCQBMpzrry8LYSENh21RqgK2jiQYFqUzcK", - "voRTejvLM85Bg8iBLSkVCuudL+6fRbB+FK46AsbHGACwsnpbCFYmrYP7gm2VrDfSAn2k3fFA+jplqRx1", - "gzCVF80M4TS+QmnyA8R3Ckz13kV65vqm/aJ5om/gFviAcf/3ccmEOPvSufMVriSMAB744V3wbgoBQGWD", - "A6FRH5wJnpL7oJs6gO4E09TX/6VoBJ044QbIaXJnmwYOmMZc269MnCCzt/YEuoX4WIHHyWWFELdVQ6zy", - "Ej9cImgLAfLOQF0Vvl9K2/wKl/pcuFVlUeUyPgCuZDsDN1BXGcCX6fYQUQBVs5UHbEIZ0aaMxsKDibKd", - "jJhr0iOYkjxsgbsJZsq9RfVEy6gldCIV1l1uYIn230hFj3akBQnElUzvLvzQ/d6uURbkfSy7ujnMoqGG", - "XeE9HzW7dSvwV1+5umwA+pmQGG63YYKrdy3+FQc3E0ZqVlDsJIX1heCEJYfXkgS2bR/09t7b5tl2c6RV", - "qu0KiFcZ3aK77ZR7iklat2N2FP1fZF7HfaNh/tNsA17FYlsS0R12BDmbNmyJbV3FbTtnpDZvnc0u45qH", - "eKSWXMTtWG3k87237dcaIXcBkPR1D1w4hjrEhH22bd0cwdXG7fW2exAXIeBT33yf94ANzovTYADfDMpy", - "+8PtKPdKb98DKJTnNIf/5EplWVx3Viy3ywB5SzcSx417bf8KXz3QXaTSxPZRKjsixsOwG4kj3iv590I0", - "0USU+/aWpqNTtOIyjatLZuy+YYs/kzjiYKpufQIOVNOt7D2Yz4Pfw6J8IuZMgZhXyxKp81Iglxwu4EQh", - "rwn5UOJKr/OjbHabPGli5MWlxGD4r3wp/bSiXEME9U6usuVlPCiZfRsdZ+fgaHptX92QU+UPW81lJ5gT", - "Hxz2ttH7temN5Rwu4UTp3wAPUFLr60nl1k7Z3L1+byZ4CqP+vfdfg/PzVwMCtBtcNLJcn4hUcqIAnQB3", - "PbB6U3L43rIi3K+9l4a30RV12fAU+ulrFGSY6JVZJoSsoLo7y7SRmwLIACeuiwP4qGIE8hVn8B8Yj/Cu", - "5NHNBJvrVLA9nTieMSzTZ0B88P2TJ/t14vbvnzxp6+Yc+Ucbu/W3w8G///r7t/0nTSkz67PO7tE9vaNn", - "JqIUfu2HNbjY/Pkc4mW3CcPL9NQelFPf/DCqpxa3X4suXxIZ4kNcJ9tBWdEmKGHEGzlSm5uZ6CzTt80x", - "IzWaywqv4LIgxOR2yJWSE4Z9Z9IGtLY1W7f9ZNqmncrYm1srP7gknrLeZzsV3+hpx+PQC9YXfQI2nS6+", - "05hffX7+qusWyjO+uDWYnolwyx2Ayc1YOsPNgp3G0izxChveqCdG2AAnTUnYkILGp1wq62rsdqZQgKWv", - "tGKZTng209Y9/fPjx48xtx1qnXHLOKg5r+6/yflUfNNn31C9/keq7hvMQfvmVoznSf7NSAW+dDsMvxxm", - "0jpAoN/b/4ZA+m21S4NbmQqGNHPArsis4rmdadfH2MJQEXC+Cl/Vnv/sTEz6iJP+H332OyOK4HOR/Af7", - "tP8NMsQCqwnRw1aZYZmbGV1MZ5RcP/cDtMwuVDIzWunChv48Pz0ehp/peCqRH3wlt4zT36sKHnxrI4WE", - "zC91KvowsX1WUjYfEz7/WKfEEE+c8Qb4uEMMEqxHubQA4u+nUKQjRWAQYf8OR2qkcLH8d2N/cEDYI6XP", - "I+OiESWoHOYODtmxosemAZEfUp4VZAMDJDxO1EQbMTVYLx9TYnF8nKoTKGLyoc6F6occRWzaRlQH7NFI", - "5SXrYoCstyILjPLwe2lZoaL/cVjKIrDHzwR7gY2/JNGHjE99q6jjsTqgzmTHE2p/gDMCRhhGoSaCaPbA", - "uzIVji6y/coilHn84BoFCavwx2CWatgiQ+opiL/T1BWA8MOuBgwN8cHrIemyRZMzlFRBuf9foqX1EL6Y", - "lbY+Uw5aQz+Aqr4xuTBuky8RUL8cAqApnUPPcW83KOmOsEN0vMMx0u67O8WvfE8eDDoxtvCZBKXWgzYR", - "KQkzDH3zRTAtJHo+Bwil8vDJOrtbggjYnN+qjTJwDl89qBBAE59XCqgLbWIAf/7MuHGrq8/vtPy/0w/g", - "bruWdXjGRlH4WQLO32ZXW1nz2htavHoXhUzvcrvfacn9aL5IMPt3P3+V4U9eHcmp4hmCKYVb5O4yiXgy", - "G6XyDD/7p5FLHM//Sub9xWACLBFnpxf/PRgjxtZ9iCfe01o9WuFgwa/+aOl84NMSB9V0UNJfvsqEEFoA", - "ZsOa3UU4UtnBtoKv/mk0FwznM9tx2IU2O+7FAnjs0Cv/1Triy/OVWZKgO0mqLtwm/3w5vbpwax31n0mn", - "3cHhHMfmi3V0PYf514XLCwceqExOBKLe/u/b7IO9zVbkXhduaz+6EQmgxk8PyhiRZg2NgBNn4fsHxfeI", - "rWzmIFjO8KeCnw/Z4zMBL0U8kNyIGwn3X4aLK1J2I1Oht3qirMgFZRy3asKQklwVjbVP98dlOFjMzQ7L", - "FqDJnI7YAn3GLcs5BNs6zSpdg8gv8p/ruT/CiCigBHZerlfaWG87RCxo3ObHdz74+Hzwy+Hgz4Nf/+3/", - "20kvw1oczPMnd04KK4WdVramXeNfB6+lknYm0sHzhsexCzkX1vF57tcCsB/rCzKhwkP2Y8ENV07gMowF", - "O3v98ttvv/3zcP2rbK0r5xirt1NPKM5v1474rjw+fLxOZwDsqswyJgGyemqEtX2WA60bc2aBXmbEpK5P", - "9xnspucT/4dVsoViOkXkAWCXA/JvqRhy+1TJ6c0Cd085iBgJ/KghEvjTVwxfgGQPFraogAD3e1FWmcSj", - "qzXXHBfbr9odTe+Ys7XuNAutIW7ASiLUyo5+QwxUJvby3pKxeZZVqt16YufcXLe/sOM4LePMa9CUEa67", - "QlmnCPjyATVWCxj1E6kAtRVlgptrYQIHzW/43ChD6gQZlyenT/yZkMx47oQJZVYTj064uX5og6XWxgOG", - "XG/Rh7a73gnMU9xo/2NMo+dpGiUTZYXe86UaBDVfyuT2ewPx4NeH/T+0GNYbWWs2P1p3BNIh+xUij8IM", - "RKKuqo55p7IF8QSEYebCsOMjlnCF7FRTaZ0wCA3NQWsNd5EDna8TA50/vBRU2tj97kRh+J+XFMrpvG4A", - "dl+QPNscDPaf/IZTAAFEf+XCWC8EyrG3OhXD32wIhmFnr07fDNl5ZKNB2oqZyPKnIzVSV1dXv9mR8s0O", - "/S/39p+xtv8ODhicxnPhZjqtlRr1gHls1GsqfnDAzuVUcVcY0Y/MG/1w0YmERAD7Xq91JezLtwC99p1/", - "fsNlBqww1CU/JKA5+5fBgL149ePxW/bjq7evzp5fvDqCmWAnry5+enfE3hyfX7DBAHnQRmoAf3zKrmLb", - "V336B4R9x3+JuXTHcz4VV77QixJ+2hmdPWVXSQolU8OlwmQG/09vOb/WBn7h/z3VTr83mf8x51MAZvI/", - "Y7gdUV6fUwCc/wPMrf/BEQ2X/3kis+xY5QX8IyfqSv8zEuJBOSQALEkJ/S8hFo+PoWPEg3vBx1DwVrpk", - "Rv9Q4pZ+SjJtBf0slC2MOBM8o1/Iib8rX0CYVBjqSXXQbzRPq3Pgd4Byld+8Fe5Wm+vjNINp/g0KFxG0", - "wP9r5lz+o3Aw5xho95RdLYcWQpXL4nJVFYhXb4+6icNI0coOaGWDfIFlx6PUAREg5scQCZZW7IoieK4w", - "lA84KCZGfxRqpJrCHZNMCuQRKivmNoyFCHpCIBn9cshe6vlcK+ZnbpLpW1vdzBxuaiRke6NeIBOgHTZM", - "9By3EX5YWag9/9tEQyySEYL9wPCTRsmsfJxJdc1+gDJDpVNhhxOp0j3/I/sBZ5X5fwyNzgT74Ycf2Kjn", - "y4x6QEv4+Hv8P34DUD34zYk2NWiy4XCIPZcTtvcvvoZ9isBU4paBJt7DmpnSjk28AVcdKmykPSj3bKRC", - "7yH7NQx1Zc/sjXoHbp4fQFhfrqajXp+Bw5s9+v7wsKy7rh/2fve1fqooqwudExXuWCp/JCAhULpQfE4s", - "mXJOcZlznRaZsEGvs0I5CdxFAmMF0W2KqpInsKOG7NWHCC15w7OCqB+IUOwZE8CUhHcQOAgq+m1vOBzu", - "X/WBVEV7HUzyDjRpS5oPvx2yU6mUSL0Gc8kM4tpIVsvo5kGijbhiOU+u+RT7M1KllFMAbpgCdoXj3/M9", - "gZqSNN/bvwo8mpYVygjrjIR71ZG4gX1PwgmRrxA76ydrLtAYtCzjHyVtTmmZvlUY9uoP5+enx2Eqh+zM", - "T24ffkfEMP0QT9wfqcRwO+uzd+9O+rQATic6izQGqe+XXlhImrcOsipUGhIg4bcv3x8fPcbJvJTp1XCk", - "YjQarlTJ6+c1h7Q1jrHa0JcDghVQh1WiB0fqLyd95q9wNzwTdJs1hd+pIIF0Iw3ud9/VSlR5WfPz02P7", - "jMnAcsYsV+lYfxDpmvBUUp1n3pB5GEOx0kLtzlq3lYBGKcRrj3UayGgyOZcOvW9/YifyRXBh4ewCRgFa", - "3hckIkb8BpRs3N+/7IxnImUpF3OtYvXwcAG+W/j9N5aqVuI2k0oMUhFapRID/2vsCgJgKq0GIEQFehme", - "HB4Oe3/ki2ttUruF2O5RjK3fERUKD9oT+3e6hj369vOG5dUt7Ep0OdgNHaNzbcIz4fRHYfRBKq1Xemu5", - "4/Fl0Df2lxPkz/E1AG6zZr6Wvr/tcZNmMOsT9tPFxSlzhk8mMvG2h3RD9pJnWYB69uILmQ7S+ipvZZax", - "W34t/JYei4QXVrD3Sl4bPnH4V1447c9aPJDgW2Suj4yeQXn85aQRqRmHee5HfqF/EUb3umRQw/cDpwd+", - "lIzmKr2XFT1OxTzXDv20VDPMqwizWpmizhfn6tIKtX5lz4R12ghLHE/YeBxsZCIse9FnPMv0LXj1Yb7r", - "3UVHPjwyyDQTuORYNr48/OWEKU1Y0cCoaem5YSay1Js61430jK/U3VcPp+MBFg8rvvvaxU82Yq1D9g3e", - "kVgsVeeF8ZYDfvzk8AmTk8p3dGhGzq/GU/NH4S5ifx5Qv8dGzr110gg93TzAXT2mK/FYLRPYZdX6JRHT", - "ktLkhtjTEWoQl6x1qcCZRi14e6DMt2FWRE1fMRoWYDKgGZc+C++01SpKRl5poqxY4Zy38LcSDnaOpZi4", - "EdWue5kPswJAQbi/nrIJz6xgSSa4sQHZvzLaBmEDUqm6uD2YeRabqfJH/XH2zM7y/hWDVhJ/1d02WtGg", - "D8+F27Czgpw/PnxUl/NbjoJeiewoZf5ZTAZ9fHjoy4E7wvqtkIkkZDvq3A2kesp4aYLMuKN94Guv7sc9", - "vsQKh5ddpd0MQynQgIELvDZhr4XtFSyP/dZt9SykQzIZzyY6drdT/KeF+3w78Yvfeff5wrh7h6z4vCli", - "53c7NmvGTgWDp9lMPVZ0z1UYo1i+XJddwJDJPptyV/pRQh7vUkerSuEQdyF8ba2cKpEyoW5EpnNRGq3U", - "rGU8DQFRjw+fNPx9IjN88dpTOjQfgqQIowu+/caWW1vacnfD1n9yeMjQVybTGp9/824dZ9KWZycGlj5Q", - "/DW2BU18pvjrcpy0SI3ZlLAcOfYWXIxhRRNuArVvud7WD0UlYoj7u+EegRXyJBE5iFfhypVeL2vP8IwJ", - "XbkDoWpt/9FKdNgS22/HlRDtZWQeAWRP8FxXj1Yu28YtPWSveDJj8BIDuA2Upz9nVzJ9yn634u+fRiOV", - "csefst/DIg28RPjfj0bqyp+4uDpE8Yu+MnIQDuZaaaeVTMBVGLzSPDHa2iWVSS7PZ4yzN9y6Aazp4PgI", - "/RlcLaIl4Auq8pSHfUh0/LaYBxcGDnvIjozOyU8KaWkoElOe22C2X8n0ClEUnoIJgs+vQt6IFP8mLYIQ", - "uxlX7BHjM8HTEMSZ+b5aIRR82g9R2rfCeFWCblcYAXhZi8lEmCF7CU83ltmZLrKUOQO0/iu1QTyocCJx", - "0N8hew1gHeXwbbBRlqYM3NBls+XtgpYKX5zg8iLgBQR7/QwCTtnVf8DzwOIHnmVXCOlZq05nKfAvwQXG", - "62OScOsET9H9eSv9fM94LgLGwlQoYWTCruqa8GrIXmsTLS+aPUHXJdq7PwOjOEJtsD3/ube8ssxLW2EJ", - "bU4nxVwoXwpeO6/2+zV1foVU5F7mtJlHROeSJ59snn+Fbh3Bx6jU+iUCxHhBlTdpegxNrw9vI8HLmRfZ", - "QPINBqKt76chewfvL4ZZoVJ22LAeYXlzbSUa1d32ZJ9ZXd9Y8PyD20j4bWbohZKa4g5j3Ibsgl8L68sl", - "IoWGwJF9hXJzhQcvPHJiw8IK5aA5r5B44fTACBLjsrlMcC+TJEgYETjAKv0KzaQFHqWS5AtDUcsI5tom", - "2A416RQEfxuBH7IzoKODLc0Sr0+4Y48OHz95BgWiMPOKJoBk/cJMeCLQfT+Rxjrc7FMA1TKkZYatXGY4", - "I81JH1m2Gx3ZHdJmOp34bzocRl8dhNPyCPyKngtzI8zg3O/HqAE6HfD4Qn+AIQjrjG2EJgLdBthEGQaX", - "VgCKIvIsalCts0sjJkP2VquB33y2GHudkook4waZ9H0/RsrBuyheraNOEtIQxhDuZp0XGXcBA5wrfP+W", - "anoJ9aIxjbmTQ/bOzSDwUGd2pOC5G0EUnTBzqXhGeBp9JlWSFRCHRdZBSb8OmDtV/5qTXl8AUxf0sQRG", - "GqmxmErEHoKH0RsI/STooMIlei4uC3Wt9C0SwSuBX7hodgKKVO01o0nZ4zJgJMaF1g/1YokNYGNrnizh", - "+ZrfsuWnS7i8VN4tH8HjItDcP2E/yxd+20Tb26twsGzoXTM+aP6hz4nliLH4mjfFmdFzWcxLux/wuGJJ", - "DA6TIICl0OKLaWUXgL2rTYlttSKcw6/Do+dLPXmwdXiNV9ymhSg/YmMx5aoPFzDpLAa5h33HEjB2/X1r", - "LJgew+70N676ww3IOsAH28SLpEir+G4dH1BJmYLe6UCMG0Dj/H5QqBArbdYCrFA5VUImKFor1IB6TNhS", - "mcYo2/GCvIY6Fwrw1ADDbcHEfCzSFOIzFLoFpZvB5URbgDnDIJV3iuhtDBgjIGf9YBxT1MpcK+m0CZeg", - "so98jOEaAWANrDmISsuM4OlipKBXmIEAQyCMOd98PxSjiMvl/vqiIsUUIagCbjR+Ly3pUQq8MUhA5C9Y", - "0Ufj50Obsm68lUI3K3PoZmLOILAQogfokBDWH0ILmCW40oISD0vhzyid878XIp6FWG88JzHQpuU47TOe", - "aTUlWL5VtkgH9x0/PX0/CJwiHfBxm8ZDcUkpPLu/t/Tb0DfAhUMzIB5I+FTrLflUww5SlAc258pfisL9", - "KRXjYjr1q1+9JPrLQzH2oj/21cVwwmB5Nb8uloeb7T24wodm1mGOvKRLThPW4/Bz5naRmlpSGCtqoovi", - "+vTp/wUAAP//x5vyWueVAwA=", + "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", + "mwMA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/server/lib/telemetry/telemetry.go b/server/lib/telemetry/telemetry.go index c155ed713..99976447e 100644 --- a/server/lib/telemetry/telemetry.go +++ b/server/lib/telemetry/telemetry.go @@ -20,6 +20,11 @@ type TelemetryConfig struct { // ExportOTLP forwards captured events to the configured OTLP endpoint. // Off by default and independent of what is captured. ExportOTLP bool + // StoreS2 writes captured events to the S2 storage sink. The API default + // is on (an omitted storage block means true), so unlike the other fields + // the zero value here is not the default: a config that leaves it unset + // does not store, which is the safe way to fail. + StoreS2 bool // ExcludedCdpMethods leaves the named browser-control methods out of the // cdp_command stream. Empty reports every supported method. Telemetry only: // an excluded command still reaches the browser. @@ -41,8 +46,13 @@ type TelemetrySession struct { sessionStartSeq uint64 categories map[oapi.TelemetryEventCategory]struct{} exportOTLP bool - appliedAt time.Time - excludedCdp map[string]struct{} + storeS2 bool + // storeS2After is the last seq published before storage was turned on for + // the current session. Everything at or below it was captured with storage + // off, so the S2 sink must start after it. + storeS2After uint64 + appliedAt time.Time + excludedCdp map[string]struct{} // active mirrors "a session is running with these categories" for callers // on a hot path, who must decide whether to do any work at all before they // reach Publish and its mutex. nil means no session. Written under mu; @@ -114,6 +124,8 @@ func (s *TelemetrySession) Start(telemetrySessionID string, cfg TelemetryConfig) s.appliedAt = time.Now() s.categories = categorySet(cfg.Categories) s.exportOTLP = cfg.ExportOTLP + s.storeS2 = cfg.StoreS2 + s.storeS2After = s.sessionStartSeq s.excludedCdp = excludedSet(cfg.ExcludedCdpMethods) s.setActiveLocked() } @@ -196,7 +208,7 @@ func (s *TelemetrySession) Config() TelemetryConfig { excluded = append(excluded, oapi.BrowserCdpCommandMethod(m)) } sort.Slice(excluded, func(i, j int) bool { return excluded[i] < excluded[j] }) - return TelemetryConfig{Categories: cats, ExportOTLP: s.exportOTLP, ExcludedCdpMethods: excluded} + return TelemetryConfig{Categories: cats, ExportOTLP: s.exportOTLP, StoreS2: s.storeS2, ExcludedCdpMethods: excluded} } // AppliedAt returns when the current configuration was applied, or the zero @@ -213,10 +225,28 @@ func (s *TelemetrySession) UpdateConfig(cfg TelemetryConfig) { defer s.mu.Unlock() s.categories = categorySet(cfg.Categories) s.exportOTLP = cfg.ExportOTLP + // Publish holds mu, so the seq read here is exactly the boundary between + // events captured with storage off and those captured with it on. + if cfg.StoreS2 && !s.storeS2 { + s.storeS2After = s.es.Seq() + } + s.storeS2 = cfg.StoreS2 s.excludedCdp = excludedSet(cfg.ExcludedCdpMethods) s.setActiveLocked() } +// StoreS2After reports whether the current session stores to S2 and, if it +// does, the seq the sink must start after. False when no session is active, +// since Stop clears storeS2. +func (s *TelemetrySession) StoreS2After() (uint64, bool) { + s.mu.Lock() + defer s.mu.Unlock() + if !s.storeS2 { + return 0, false + } + return s.storeS2After, true +} + // CategoryEnabled reports whether events in category c are currently captured. // It returns false when no session is active. Lock-free, so a caller on the // CDP forwarding path can check it per frame; Publish re-checks under mu. @@ -252,8 +282,9 @@ func (s *TelemetrySession) Stop() { defer s.mu.Unlock() s.id = "" s.appliedAt = time.Time{} - // The session is over, so export is off; keep Config() authoritative for the - // desired export state after a clear. + // The session is over, so export and storage are off; keep Config() + // authoritative for the desired sink state after a clear. s.exportOTLP = false + s.storeS2 = false s.setActiveLocked() } diff --git a/server/lib/telemetry/telemetry_test.go b/server/lib/telemetry/telemetry_test.go index 18d2929dd..32a3f9041 100644 --- a/server/lib/telemetry/telemetry_test.go +++ b/server/lib/telemetry/telemetry_test.go @@ -232,3 +232,71 @@ func TestTelemetrySession(t *testing.T) { assert.True(t, json.Valid(env.Event.Data)) }) } + +func TestTelemetrySessionStoreS2(t *testing.T) { + ts := newTestTelemetrySession(t) + assert.False(t, ts.Config().StoreS2, "nothing is stored before a session") + + ts.Start("session-1", TelemetryConfig{StoreS2: true}) + assert.True(t, ts.Config().StoreS2) + + ts.UpdateConfig(TelemetryConfig{StoreS2: false}) + assert.False(t, ts.Config().StoreS2) + + ts.UpdateConfig(TelemetryConfig{StoreS2: true}) + ts.Stop() + assert.False(t, ts.Config().StoreS2, "a clear must leave the desired storage state off") +} + +func TestTelemetrySessionStoreS2After(t *testing.T) { + ts := newTestTelemetrySession(t) + _, store := ts.StoreS2After() + assert.False(t, store, "no session, nothing to store") + + ts.Start("off", TelemetryConfig{Categories: events.UserCategories}) + ts.Publish(cdpEvent("ev.one", events.Network)) + ts.Publish(cdpEvent("ev.two", events.Network)) + _, store = ts.StoreS2After() + assert.False(t, store) + + ts.UpdateConfig(TelemetryConfig{Categories: events.UserCategories, StoreS2: true}) + after, store := ts.StoreS2After() + assert.True(t, store) + assert.EqualValues(t, 2, after, "turning storage on mid-session starts after what was captured with it off") + + ts.Publish(cdpEvent("ev.three", events.Network)) + ts.UpdateConfig(TelemetryConfig{Categories: events.UserCategories, StoreS2: true}) + after, _ = ts.StoreS2After() + assert.EqualValues(t, 2, after, "a config that keeps storage on keeps its floor") + + ts.Stop() + _, store = ts.StoreS2After() + assert.False(t, store) + + ts.Start("on", TelemetryConfig{Categories: events.UserCategories, StoreS2: true}) + after, store = ts.StoreS2After() + assert.True(t, store) + assert.EqualValues(t, 3, after, "a storing session starts after what earlier sessions captured") +} + +// The S2 sink opens after a storing session is committed and reads from the +// seq that session started at. Deferring the open is lossless only because +// nothing reaches the ring without a session: TelemetrySession is its only +// publisher. +func TestPublishWithoutSessionReachesNothing(t *testing.T) { + es := newTestEventStream(t, 16) + ts := NewTelemetrySession(es) + + for _, cat := range events.UserCategories { + _, ok := ts.Publish(cdpEvent("before.session", cat)) + assert.False(t, ok) + } + assert.Zero(t, es.Seq(), "nothing may reach the ring before a session") + + ts.Start("session-1", TelemetryConfig{Categories: events.UserCategories, StoreS2: true}) + ts.Publish(cdpEvent("in.session", events.Network)) + ts.Stop() + _, ok := ts.Publish(cdpEvent("after.session", events.Network)) + assert.False(t, ok) + assert.Equal(t, uint64(1), es.Seq(), "nothing may reach the ring after a clear either") +} diff --git a/server/openapi.yaml b/server/openapi.yaml index dd3056671..21f959a6e 100644 --- a/server/openapi.yaml +++ b/server/openapi.yaml @@ -1547,7 +1547,9 @@ paths: previously configured; returns 200 if it was. Selection is opt-in: a request that enables no categories (a browser config with nothing set to true, or every category disabled) clears the configuration; this is - idempotent when telemetry is not configured. + idempotent when telemetry is not configured. Returns 409 when + `storage.enabled` is false on an instance that has already started + storage; the configuration is left unchanged. operationId: putTelemetry x-telemetry-category: platform requestBody: @@ -1571,6 +1573,8 @@ paths: $ref: "#/components/schemas/TelemetryState" "400": $ref: "#/components/responses/BadRequestError" + "409": + $ref: "#/components/responses/ConflictError" "500": $ref: "#/components/responses/InternalError" patch: @@ -1580,6 +1584,8 @@ paths: set in the request body are changed; omitted categories retain their current settings. Returns 404 if telemetry is not configured. Setting every configurable category to enabled: false clears the configuration. + Returns 409 when `storage.enabled` is false on an instance that has + already started storage; the configuration is left unchanged. operationId: patchTelemetry x-telemetry-category: platform requestBody: @@ -1599,6 +1605,8 @@ paths: $ref: "#/components/responses/BadRequestError" "404": $ref: "#/components/responses/NotFoundError" + "409": + $ref: "#/components/responses/ConflictError" "500": $ref: "#/components/responses/InternalError" /telemetry/events: @@ -1870,6 +1878,8 @@ components: $ref: "#/components/schemas/BrowserTelemetryCategoriesConfig" export: $ref: "#/components/schemas/BrowserTelemetryExportConfig" + storage: + $ref: "#/components/schemas/BrowserTelemetryStorageConfig" additionalProperties: false BrowserTelemetryExportConfig: type: object @@ -1894,6 +1904,26 @@ components: destination. Off by default. Has no effect (export stays inactive) when no export destination is configured. additionalProperties: false + BrowserTelemetryStorageConfig: + type: object + description: > + Durable storage of captured telemetry in Kernel's event store. On by + default, so a caller that omits this block keeps today's behavior: in a + PUT (full replace) an omitted block means enabled, unlike `export`, + whose omitted block means off; in a PATCH an omitted field leaves the + current setting unchanged. Only events captured while storage is enabled + are stored. Storage opens with the first capture session that has it + enabled and stays open for the life of the instance, so once it has + started a request that disables it is rejected with 409. + properties: + enabled: + type: boolean + description: > + Whether captured telemetry is written to Kernel's event store. + Defaults to true. Set false to keep events on the live stream and + any enabled export destination only. Cannot be set false on an + instance that has already started storage (409). + additionalProperties: false BrowserTelemetryCategoriesConfig: type: object description: Per-category telemetry capture settings for browser events.