package server import ( "context" "fmt" "net/http" "sync/atomic" "time" "chat-agent-system/internal/artifactvault" "chat-agent-system/internal/config" ) // Vault HTTP + in-process publisher stubs for U7 incremental. // Default: FAV_PUBLISHER_ENABLED=false, FAV_DEGRADED_READ=false. // Full ingest hooks / enumerate backfill remain on feature/final-artifact-vault. func (s *Server) initArtifactVault() { if s == nil || s.cfg == nil { return } // Publisher store stays nil unless explicitly enabled AND vault roots are set. if !s.cfg.FAVPublisherEnabled { return } if s.cfg.VaultStateRoot == "" || s.cfg.VaultMasterKey == "" { return } if err := config.ValidateIndependentVault(s.cfg.VaultStateRoot, s.cfg.DataDir); err != nil { return } ident := s.cfg.VaultStorageIdentity if ident == "" { env := s.cfg.Env if env == "" { env = "dev" } ident = "vault-" + env } store, err := artifactvault.NewFilesystemStore(s.cfg.VaultStateRoot, ident, []byte(s.cfg.VaultMasterKey)) if err != nil { return } var reg artifactvault.Registry if s.pool != nil { reg = artifactvault.NewPGRegistry(s.pool) } else { reg = artifactvault.NewMemoryRegistry() } s.vaultStore = store s.vaultReg = reg s.vaultPub = &artifactvault.Publisher{Reg: reg, Store: store} } func (s *Server) startVaultPublisher() { if s == nil || s.vaultPub == nil || s.cfg == nil || !s.cfg.FAVPublisherEnabled { return } go func() { t := time.NewTicker(2 * time.Second) defer t.Stop() for range t.C { _ = s.vaultPub.Drain(context.Background()) } }() atomic.StoreInt32(&s.vaultPublisherStarted, 1) } func (s *Server) tryVaultDegraded(ctx context.Context, env, logical, projectKey string) ([]byte, string, error) { if s == nil || s.vaultPub == nil || s.cfg == nil || !s.cfg.FAVDegradedRead { return nil, "", fmt.Errorf("artifactvault: degraded read disabled") } return s.vaultPub.DegradedPayload(ctx, env, logical, projectKey) } func (s *Server) handleVaultStatus(w http.ResponseWriter, r *http.Request) { _ = r cfg := s.cfg degraded := false publisher := false storeReady := false if cfg != nil { degraded = cfg.FAVDegradedRead publisher = cfg.FAVPublisherEnabled } if s.vaultStore != nil { storeReady = true } writeJSONResponse(w, http.StatusOK, map[string]any{ "status": "ok", "consumer": "fav", "consumer_mode": "off", "publisher_enabled": publisher, "publisher_running": atomic.LoadInt32(&s.vaultPublisherStarted) == 1, "degraded_read": degraded, "store_configured": storeReady, "subscriber_default": false, "shadow_observe": true, "would_stage": false, "would_publish": false, "live_not_opened": true, "note": "fav consumer remains OFF until separate approval; shadow computes intended outcome only; full FAV branch not merged", }) } func (s *Server) vaultPublisherRunning() bool { return atomic.LoadInt32(&s.vaultPublisherStarted) == 1 }