import { useEffect, useMemo, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { AlertCircle, AlertTriangle, ArchiveRestore, Archive, Ban, CheckCircle2, Cloud, Database, Edit3, ExternalLink, KeyRound, Link2, Loader2, Plus, RefreshCw, Search, ShieldCheck, Star, Trash2, X, Filter, Info, } from "lucide-react"; import { Link } from "react-router-dom"; import type { CompanySecret, CompanySecretUsageBinding, CompanySecretProviderConfig, SecretAccessEvent, SecretManagedMode, SecretProvider, SecretProviderConfigStatus, SecretProviderDescriptor, SecretStatus, } from "@paperclipai/shared"; import { useCompany } from "../context/CompanyContext"; import { useBreadcrumbs } from "../context/BreadcrumbContext"; import { useToastActions } from "../context/ToastContext"; import { secretsApi, type CreateSecretInput, type CreateSecretProviderConfigInput, type SecretProviderHealthResponse, type UpdateSecretProviderConfigInput, } from "../api/secrets"; import { ApiError } from "../api/client"; import { queryKeys } from "../lib/queryKeys"; import { EmptyState } from "../components/EmptyState"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Checkbox } from "@/components/ui/checkbox"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, } from "@/components/ui/sheet"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; import { Badge } from "@/components/ui/badge"; import { cn } from "../lib/utils"; import { PageTabBar } from "../components/PageTabBar"; import { ImportFromVaultDialog } from "./secrets/ImportFromVaultDialog"; type CreateMode = "managed" | "external"; type SecretsTab = "secrets" | "vaults"; type ProviderVaultForm = { provider: SecretProvider; displayName: string; status: SecretProviderConfigStatus; isDefault: boolean; backupReminderAcknowledged: boolean; region: string; namespace: string; secretNamePrefix: string; kmsKeyId: string; ownerTag: string; environmentTag: string; projectId: string; location: string; address: string; mountPath: string; secretPathPrefix: string; }; const PROVIDER_ORDER: SecretProvider[] = [ "local_encrypted", "aws_secrets_manager", "gcp_secret_manager", "vault", ]; function defaultProviderVaultStatus(provider: SecretProvider): SecretProviderConfigStatus { return provider === "gcp_secret_manager" || provider === "vault" ? "coming_soon" : "ready"; } function emptyProviderVaultForm(provider: SecretProvider = "local_encrypted"): ProviderVaultForm { return { provider, displayName: "", status: defaultProviderVaultStatus(provider), isDefault: false, backupReminderAcknowledged: false, region: "", namespace: "", secretNamePrefix: "", kmsKeyId: "", ownerTag: "", environmentTag: "", projectId: "", location: "", address: "", mountPath: "", secretPathPrefix: "", }; } function providerConfigValue(config: CompanySecretProviderConfig["config"], key: string) { if (!config || typeof config !== "object" || Array.isArray(config)) return ""; const value = (config as Record)[key]; return typeof value === "string" ? value : ""; } function providerVaultFormFromConfig(config: CompanySecretProviderConfig): ProviderVaultForm { return { ...emptyProviderVaultForm(config.provider), displayName: config.displayName, status: config.status, isDefault: config.isDefault, backupReminderAcknowledged: Boolean((config.config as Record | undefined)?.backupReminderAcknowledged), region: providerConfigValue(config.config, "region"), namespace: providerConfigValue(config.config, "namespace"), secretNamePrefix: providerConfigValue(config.config, "secretNamePrefix"), kmsKeyId: providerConfigValue(config.config, "kmsKeyId"), ownerTag: providerConfigValue(config.config, "ownerTag"), environmentTag: providerConfigValue(config.config, "environmentTag"), projectId: providerConfigValue(config.config, "projectId"), location: providerConfigValue(config.config, "location"), address: providerConfigValue(config.config, "address"), mountPath: providerConfigValue(config.config, "mountPath"), secretPathPrefix: providerConfigValue(config.config, "secretPathPrefix"), }; } function formatRelative(value: Date | string | null | undefined): string { if (!value) return "—"; const date = typeof value === "string" ? new Date(value) : value; if (Number.isNaN(date.getTime())) return "—"; const diff = Date.now() - date.getTime(); if (diff < 0) return date.toLocaleString(); const seconds = Math.floor(diff / 1000); if (seconds < 60) return `${seconds}s ago`; const minutes = Math.floor(seconds / 60); if (minutes < 60) return `${minutes}m ago`; const hours = Math.floor(minutes / 60); if (hours < 48) return `${hours}h ago`; const days = Math.floor(hours / 24); if (days < 30) return `${days}d ago`; return date.toLocaleDateString(); } function statusTextTone(status: SecretStatus) { switch (status) { case "active": return "text-emerald-700 dark:text-emerald-300"; case "disabled": return "text-amber-700 dark:text-amber-300"; case "archived": return "text-muted-foreground"; case "deleted": return "text-destructive"; default: return "text-muted-foreground"; } } function providerLabel(providers: SecretProviderDescriptor[] | undefined, id: SecretProvider) { return providers?.find((p) => p.id === id)?.label ?? id.replaceAll("_", " "); } function normalizeSecretKeyForPreview(input: string) { return input .trim() .toLowerCase() .replace(/[^a-z0-9_.-]+/g, "-") .replace(/^-+|-+$/g, "") .slice(0, 120); } function modeLabel(managedMode: SecretManagedMode) { return managedMode === "paperclip_managed" ? "Paperclip-managed" : "Linked external"; } function modeDescription(managedMode: SecretManagedMode) { return managedMode === "paperclip_managed" ? "Paperclip owns create and rotation writes for this provider secret." : "Paperclip resolves this provider reference but does not rotate the provider value."; } function healthEntryForProvider( health: SecretProviderHealthResponse | null, providerId: SecretProvider, ) { return health?.providers.find((entry) => entry.provider === providerId) ?? null; } export function getCreateProviderBlockReason( provider: SecretProviderDescriptor | null | undefined, mode: CreateMode, health: SecretProviderHealthResponse | null, ) { if (!provider) return "Select a provider."; if (mode === "managed" && provider.supportsManagedValues === false) { return `${provider.label} does not support Paperclip-managed secret values.`; } if (mode === "external" && provider.supportsExternalReferences === false) { return `${provider.label} does not support linked external references.`; } if (provider.configured === false) { const healthEntry = healthEntryForProvider(health, provider.id); return healthEntry?.message ? `${provider.label} is not configured in this deployment. ${healthEntry.message}` : `${provider.label} is not configured in this deployment.`; } const healthEntry = healthEntryForProvider(health, provider.id); if (healthEntry?.status === "error") { return `${provider.label} health check failed: ${healthEntry.message}`; } return null; } function providerHealthText( provider: SecretProviderDescriptor | null | undefined, health: SecretProviderHealthResponse | null, ) { if (!provider) return null; const entry = healthEntryForProvider(health, provider.id); if (!entry) return null; const warnings = entry.warnings?.join(" "); return [entry.message, warnings].filter(Boolean).join(" "); } function detailString(details: Record | undefined, key: string) { const value = details?.[key]; return typeof value === "string" && value.trim() ? value.trim() : null; } export function getProviderConfigBlockReason( config: CompanySecretProviderConfig | null | undefined, ) { if (!config) return null; if (config.status === "disabled") return "This provider vault is disabled."; if (config.status === "coming_soon") return "This provider vault is saved as draft metadata only."; if (config.healthStatus === "error") { return config.healthMessage ?? "This provider vault health check failed."; } return null; } export function getDefaultProviderConfigId( configs: CompanySecretProviderConfig[], provider: SecretProvider, ) { const providerConfigs = configs.filter((config) => config.provider === provider); const selectable = providerConfigs.filter((config) => !getProviderConfigBlockReason(config)); return ( selectable.find((config) => config.isDefault)?.id ?? selectable[0]?.id ?? providerConfigs.find((config) => config.isDefault)?.id ?? "" ); } function providerVaultLabel(configs: CompanySecretProviderConfig[], id: string | null | undefined) { if (!id) return "Deployment default"; return configs.find((config) => config.id === id)?.displayName ?? "Unknown vault"; } function buildProviderVaultConfig(form: ProviderVaultForm): Record { const compact = (value: string) => value.trim() || null; switch (form.provider) { case "local_encrypted": return { backupReminderAcknowledged: form.backupReminderAcknowledged }; case "aws_secrets_manager": return { region: form.region.trim(), namespace: compact(form.namespace), secretNamePrefix: compact(form.secretNamePrefix), kmsKeyId: compact(form.kmsKeyId), ownerTag: compact(form.ownerTag), environmentTag: compact(form.environmentTag), }; case "gcp_secret_manager": return { projectId: compact(form.projectId), location: compact(form.location), namespace: compact(form.namespace), secretNamePrefix: compact(form.secretNamePrefix), }; case "vault": return { address: compact(form.address), namespace: compact(form.namespace), mountPath: compact(form.mountPath), secretPathPrefix: compact(form.secretPathPrefix), }; default: return {}; } } export function getAwsManagedPathPreview(input: { provider: SecretProviderDescriptor | null | undefined; health: SecretProviderHealthResponse | null; companyId: string; secretKeySource: string; }) { if (input.provider?.id !== "aws_secrets_manager") return null; const healthEntry = healthEntryForProvider(input.health, "aws_secrets_manager"); const prefix = detailString(healthEntry?.details, "prefix") ?? "paperclip"; const deploymentId = detailString(healthEntry?.details, "deploymentId") ?? "{deploymentId}"; const secretKey = normalizeSecretKeyForPreview(input.secretKeySource) || "{secretKey}"; return `${prefix}/${deploymentId}/${input.companyId}/${secretKey}`; } export function Secrets() { const queryClient = useQueryClient(); const { selectedCompanyId } = useCompany(); const { setBreadcrumbs } = useBreadcrumbs(); const { pushToast } = useToastActions(); const [activeTab, setActiveTab] = useState("secrets"); const [secretDetailTab, setSecretDetailTab] = useState("details"); const [search, setSearch] = useState(""); const [statusFilter, setStatusFilter] = useState("active"); const [providerFilter, setProviderFilter] = useState("all"); const [selectedSecretId, setSelectedSecretId] = useState(null); const [usageDialogSecretId, setUsageDialogSecretId] = useState(null); const [createOpen, setCreateOpen] = useState(false); const [importOpen, setImportOpen] = useState(false); const [createMode, setCreateMode] = useState("managed"); const [createForm, setCreateForm] = useState({ name: "", key: "", value: "", description: "", externalRef: "", provider: "local_encrypted" as SecretProvider, providerConfigId: "", }); const [createError, setCreateError] = useState(null); const [rotateOpen, setRotateOpen] = useState(false); const [rotateValue, setRotateValue] = useState(""); const [rotateExternalRef, setRotateExternalRef] = useState(""); const [rotateProviderConfigId, setRotateProviderConfigId] = useState(""); const [rotateError, setRotateError] = useState(null); const [deleteConfirm, setDeleteConfirm] = useState(null); const [vaultDialogOpen, setVaultDialogOpen] = useState(false); const [editingVault, setEditingVault] = useState(null); const [vaultForm, setVaultForm] = useState(() => emptyProviderVaultForm()); const [vaultError, setVaultError] = useState(null); useEffect(() => { setBreadcrumbs([{ label: "Secrets" }]); }, [setBreadcrumbs]); const secretsQuery = useQuery({ queryKey: selectedCompanyId ? queryKeys.secrets.list(selectedCompanyId) : ["secrets", "__disabled__"], queryFn: () => secretsApi.list(selectedCompanyId!), enabled: Boolean(selectedCompanyId), }); const providersQuery = useQuery({ queryKey: selectedCompanyId ? queryKeys.secrets.providers(selectedCompanyId) : ["secret-providers", "__disabled__"], queryFn: () => secretsApi.providers(selectedCompanyId!), enabled: Boolean(selectedCompanyId), staleTime: 5 * 60_000, }); const providerHealthQuery = useQuery({ queryKey: selectedCompanyId ? ["secret-provider-health", selectedCompanyId] : ["secret-provider-health", "__disabled__"], queryFn: () => secretsApi.providerHealth(selectedCompanyId!), enabled: Boolean(selectedCompanyId), refetchInterval: 60_000, retry: false, }); const providerConfigsQuery = useQuery({ queryKey: selectedCompanyId ? queryKeys.secrets.providerConfigs(selectedCompanyId) : ["secret-provider-configs", "__disabled__"], queryFn: () => secretsApi.providerConfigs(selectedCompanyId!), enabled: Boolean(selectedCompanyId), retry: false, }); const secrets = secretsQuery.data ?? []; const providers = providersQuery.data ?? []; const providerConfigs = providerConfigsQuery.data ?? []; const selectedSecret = useMemo( () => secrets.find((secret) => secret.id === selectedSecretId) ?? null, [secrets, selectedSecretId], ); const usageDialogSecret = useMemo( () => secrets.find((secret) => secret.id === usageDialogSecretId) ?? null, [secrets, usageDialogSecretId], ); const selectedCreateProvider = useMemo( () => providers.find((provider) => provider.id === createForm.provider) ?? null, [providers, createForm.provider], ); const createProviderConfigs = useMemo( () => providerConfigs.filter((config) => config.provider === createForm.provider), [createForm.provider, providerConfigs], ); const selectedCreateProviderConfig = useMemo( () => providerConfigs.find((config) => config.id === createForm.providerConfigId) ?? null, [createForm.providerConfigId, providerConfigs], ); const selectedRotateProviderConfigs = useMemo( () => providerConfigs.filter((config) => config.provider === selectedSecret?.provider), [providerConfigs, selectedSecret?.provider], ); const selectedRotateProviderConfig = useMemo( () => providerConfigs.find((config) => config.id === rotateProviderConfigId) ?? null, [providerConfigs, rotateProviderConfigId], ); const createProviderBlockReason = getCreateProviderBlockReason( selectedCreateProvider, createMode, providerHealthQuery.data ?? null, ) ?? getProviderConfigBlockReason(selectedCreateProviderConfig); const rotateProviderBlockReason = getProviderConfigBlockReason(selectedRotateProviderConfig); const createProviderHealthText = providerHealthText( selectedCreateProvider, providerHealthQuery.data ?? null, ); const awsManagedPathPreview = getAwsManagedPathPreview({ provider: selectedCreateProvider, health: providerHealthQuery.data ?? null, companyId: selectedCompanyId ?? "{companyId}", secretKeySource: createForm.key.trim() || createForm.name, }); const filtered = useMemo(() => { const needle = search.trim().toLowerCase(); return secrets.filter((secret) => { if (statusFilter !== "all" && secret.status !== statusFilter) return false; if (providerFilter !== "all" && secret.provider !== providerFilter) return false; if (!needle) return true; return ( secret.name.toLowerCase().includes(needle) || secret.key.toLowerCase().includes(needle) || (secret.description?.toLowerCase().includes(needle) ?? false) || (secret.externalRef?.toLowerCase().includes(needle) ?? false) ); }); }, [secrets, search, statusFilter, providerFilter]); const activeSecretFilterCount = (statusFilter === "active" ? 0 : 1) + (providerFilter === "all" ? 0 : 1); const usageQuery = useQuery({ queryKey: selectedSecret ? queryKeys.secrets.usage(selectedSecret.id) : ["secrets", "usage", "__disabled__"], queryFn: () => secretsApi.usage(selectedSecret!.id), enabled: Boolean(selectedSecret), }); const eventsQuery = useQuery({ queryKey: selectedSecret ? queryKeys.secrets.accessEvents(selectedSecret.id) : ["secrets", "access-events", "__disabled__"], queryFn: () => secretsApi.accessEvents(selectedSecret!.id), enabled: Boolean(selectedSecret), }); const usageDialogQuery = useQuery({ queryKey: usageDialogSecret ? queryKeys.secrets.usage(usageDialogSecret.id) : ["secrets", "usage-dialog", "__disabled__"], queryFn: () => secretsApi.usage(usageDialogSecret!.id), enabled: Boolean(usageDialogSecret), }); function invalidateAll(extraIds: string[] = []) { if (!selectedCompanyId) return; queryClient.invalidateQueries({ queryKey: queryKeys.secrets.list(selectedCompanyId) }); queryClient.invalidateQueries({ queryKey: queryKeys.secrets.providerConfigs(selectedCompanyId) }); for (const id of extraIds) { queryClient.invalidateQueries({ queryKey: queryKeys.secrets.usage(id) }); queryClient.invalidateQueries({ queryKey: queryKeys.secrets.accessEvents(id) }); } } const createMutation = useMutation({ mutationFn: () => { const input: CreateSecretInput = { name: createForm.name.trim(), provider: createForm.provider, providerConfigId: createForm.providerConfigId || null, managedMode: createMode === "external" ? "external_reference" : "paperclip_managed", description: createForm.description.trim() || null, }; if (createForm.key.trim()) input.key = createForm.key.trim(); if (createMode === "managed") { input.value = createForm.value; } else { input.externalRef = createForm.externalRef.trim(); } return secretsApi.create(selectedCompanyId!, input); }, onSuccess: (created) => { pushToast({ title: "Secret created", body: created.name, tone: "success" }); setCreateOpen(false); setCreateForm({ name: "", key: "", value: "", description: "", externalRef: "", provider: createForm.provider, providerConfigId: getDefaultProviderConfigId(providerConfigs, createForm.provider), }); setCreateError(null); setSelectedSecretId(created.id); invalidateAll([created.id]); }, onError: (error) => { setCreateError(error instanceof ApiError ? error.message : (error as Error).message); }, }); const rotateMutation = useMutation({ mutationFn: () => { if (!selectedSecret) throw new Error("Select a secret first"); if (selectedSecret.managedMode === "external_reference") { return secretsApi.rotate(selectedSecret.id, { externalRef: rotateExternalRef.trim() || selectedSecret.externalRef || undefined, providerConfigId: rotateProviderConfigId || null, }); } return secretsApi.rotate(selectedSecret.id, { value: rotateValue, providerConfigId: rotateProviderConfigId || null, }); }, onSuccess: (updated) => { pushToast({ title: "Rotated", body: `${updated.name} → v${updated.latestVersion}`, tone: "success" }); setRotateOpen(false); setRotateValue(""); setRotateExternalRef(""); setRotateProviderConfigId(""); setRotateError(null); invalidateAll([updated.id]); }, onError: (error) => { setRotateError(error instanceof Error ? error.message : "Rotate failed"); }, }); const statusMutation = useMutation({ mutationFn: ({ id, status }: { id: string; status: SecretStatus }) => { switch (status) { case "active": return secretsApi.enable(id); case "disabled": return secretsApi.disable(id); case "archived": return secretsApi.archive(id); default: return secretsApi.update(id, { status }); } }, onSuccess: (updated) => { pushToast({ title: `Secret ${updated.status}`, body: updated.name, tone: "info" }); invalidateAll([updated.id]); }, onError: (error) => { pushToast({ title: "Status update failed", body: error instanceof Error ? error.message : "Try again", tone: "error", }); }, }); const deleteMutation = useMutation({ mutationFn: (id: string) => secretsApi.remove(id), onSuccess: (_response, id) => { pushToast({ title: "Secret deleted", tone: "info" }); setDeleteConfirm(null); if (selectedSecretId === id) setSelectedSecretId(null); invalidateAll([id]); }, onError: (error) => { pushToast({ title: "Delete failed", body: error instanceof Error ? error.message : "Try again", tone: "error", }); }, }); const saveVaultMutation = useMutation({ mutationFn: () => { const data: CreateSecretProviderConfigInput | UpdateSecretProviderConfigInput = { displayName: vaultForm.displayName.trim(), status: vaultForm.status, isDefault: vaultForm.isDefault, config: buildProviderVaultConfig(vaultForm), }; if (editingVault) { return secretsApi.updateProviderConfig(editingVault.id, data); } return secretsApi.createProviderConfig(selectedCompanyId!, { ...(data as UpdateSecretProviderConfigInput), provider: vaultForm.provider, } as CreateSecretProviderConfigInput); }, onSuccess: (saved) => { pushToast({ title: editingVault ? "Provider vault updated" : "Provider vault created", body: saved.displayName, tone: "success" }); setVaultDialogOpen(false); setEditingVault(null); setVaultForm(emptyProviderVaultForm()); setVaultError(null); invalidateAll(); }, onError: (error) => { setVaultError(error instanceof ApiError ? error.message : (error as Error).message); }, }); const disableVaultMutation = useMutation({ mutationFn: (id: string) => secretsApi.disableProviderConfig(id), onSuccess: (updated) => { pushToast({ title: "Provider vault disabled", body: updated.displayName, tone: "info" }); invalidateAll(); }, onError: (error) => { pushToast({ title: "Disable failed", body: error instanceof Error ? error.message : "Try again", tone: "error", }); }, }); const defaultVaultMutation = useMutation({ mutationFn: (id: string) => secretsApi.setDefaultProviderConfig(id), onSuccess: (updated) => { pushToast({ title: "Default vault set", body: updated.displayName, tone: "success" }); invalidateAll(); }, onError: (error) => { pushToast({ title: "Default update failed", body: error instanceof Error ? error.message : "Try again", tone: "error", }); }, }); const healthVaultMutation = useMutation({ mutationFn: (id: string) => secretsApi.checkProviderConfigHealth(id), onSuccess: (health) => { pushToast({ title: "Health checked", body: health.message, tone: health.status === "error" ? "error" : "info" }); invalidateAll(); }, onError: (error) => { pushToast({ title: "Health check failed", body: error instanceof Error ? error.message : "Try again", tone: "error", }); }, }); useEffect(() => { if (!createOpen || providers.length === 0) return; const currentBlockReason = getCreateProviderBlockReason( providers.find((provider) => provider.id === createForm.provider) ?? null, createMode, providerHealthQuery.data ?? null, ); if (!currentBlockReason) return; const replacement = providers.find( (provider) => !getCreateProviderBlockReason(provider, createMode, providerHealthQuery.data ?? null), ); if (replacement && replacement.id !== createForm.provider) { setCreateForm((current) => ({ ...current, provider: replacement.id, providerConfigId: getDefaultProviderConfigId(providerConfigs, replacement.id), })); } }, [createForm.provider, createMode, createOpen, providerConfigs, providerHealthQuery.data, providers]); useEffect(() => { if (!createOpen) return; const current = providerConfigs.find((config) => config.id === createForm.providerConfigId); if (current?.provider === createForm.provider) return; setCreateForm((form) => ({ ...form, providerConfigId: getDefaultProviderConfigId(providerConfigs, form.provider), })); }, [createForm.provider, createForm.providerConfigId, createOpen, providerConfigs]); useEffect(() => { if (!rotateOpen || !selectedSecret) return; setRotateProviderConfigId( selectedSecret.providerConfigId ?? getDefaultProviderConfigId(providerConfigs, selectedSecret.provider), ); }, [providerConfigs, rotateOpen, selectedSecret]); function openCreateVault(provider: SecretProvider = "local_encrypted") { setEditingVault(null); setVaultForm(emptyProviderVaultForm(provider)); setVaultError(null); setVaultDialogOpen(true); } function openEditVault(config: CompanySecretProviderConfig) { setEditingVault(config); setVaultForm(providerVaultFormFromConfig(config)); setVaultError(null); setVaultDialogOpen(true); } if (!selectedCompanyId) { return (
Select a company to manage secrets.
); } return (

Secrets

setActiveTab(value as SecretsTab)} className="flex min-h-0 flex-1 flex-col gap-4" > setActiveTab(value as SecretsTab)} />
setSearch(event.target.value)} placeholder="Search by name, key, ref" className="pl-7 text-xs sm:text-sm" aria-label="Search secrets" data-page-search-target="true" />
setImportOpen(true)} onManageVaults={() => setActiveTab("vaults")} className="ml-auto" />
{secretsQuery.isError ? (
Failed to load secrets:{" "} {(secretsQuery.error as Error).message}
) : secrets.length === 0 && !secretsQuery.isPending ? ( setCreateOpen(true)} /> ) : filtered.length === 0 ? ( ) : ( {filtered.map((secret) => ( setSelectedSecretId(secret.id)} > ))}
Name Mode Provider Status Version Last rotated Last resolved References Reference
{secret.name}
{modeLabel(secret.managedMode)}
{providerLabel(providers, secret.provider)}
{secret.status} v{secret.latestVersion} {formatRelative(secret.lastRotatedAt)} {formatRelative(secret.lastResolvedAt)} {secret.managedMode === "external_reference" ? ( {secret.externalRef ?? "—"} ) : ( Owned )}
)}
providerConfigsQuery.refetch()} onCreate={openCreateVault} onEdit={openEditVault} onDisable={(config) => disableVaultMutation.mutate(config.id)} onSetDefault={(config) => defaultVaultMutation.mutate(config.id)} onHealthCheck={(config) => healthVaultMutation.mutate(config.id)} pendingActionId={ disableVaultMutation.variables ?? defaultVaultMutation.variables ?? healthVaultMutation.variables ?? null } />
!open && setSelectedSecretId(null)}> {selectedSecret ? ( <> {selectedSecret.name} {selectedSecret.status} {providerLabel(providers, selectedSecret.provider)} · v{selectedSecret.latestVersion} · {modeLabel(selectedSecret.managedMode)}
{selectedSecret.status === "active" ? ( ) : ( )} {selectedSecret.status === "archived" ? ( ) : ( )}
) : null}
!open && setUsageDialogSecretId(null)} > Secret references {usageDialogSecret ? `${usageDialogSecret.name} is referenced by ${usageDialogSecret.referenceCount ?? 0} ${ (usageDialogSecret.referenceCount ?? 0) === 1 ? "place" : "places" }.` : null} {selectedCompanyId && ( { setImportOpen(false); setActiveTab("vaults"); }} onImportComplete={() => { void secretsQuery.refetch(); }} /> )} Create secret Choose whether Paperclip should own future provider writes, or only resolve an existing provider reference at runtime. setCreateMode(value as CreateMode)}> Managed value External reference
setCreateForm((current) => ({ ...current, name: event.target.value })) } placeholder="OPENAI_API_KEY" autoFocus />
setCreateForm((current) => ({ ...current, key: event.target.value })) } placeholder="auto from name" />
{createProviderBlockReason ? (

{createProviderBlockReason}

) : createProviderHealthText ? (

{createProviderHealthText}

) : null}
{selectedCreateProviderConfig ? ( ) : (

Existing deployment-level provider settings stay available for backwards compatibility.

)}
{createMode === "managed" ? ( <>
Paperclip-managed secrets are created in the selected provider and future rotations write a new provider version through Paperclip. {awsManagedPathPreview ? (
AWS managed path:{" "} {awsManagedPathPreview}
) : null}