import { Helmet } from "react-helmet"; import { useTranslation } from "react-i18next"; import { faArrowsSpin, faArrowUpRightFromSquare, faExclamationTriangle, faFolder, faInfoCircle, faPlus, faRotate, faTrash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Link, useNavigate } from "@tanstack/react-router"; import { formatDistance } from "date-fns"; import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal"; import { createNotification } from "@app/components/notifications"; import { ProjectPermissionCan } from "@app/components/permissions"; import { Button, DeleteActionModal, EmptyState, IconButton, Modal, ModalContent, PageHeader, Skeleton, Spinner, Table, TableContainer, TableSkeleton, TBody, Td, Th, THead, Tooltip, Tr } from "@app/components/v2"; import { NoticeBannerV2 } from "@app/components/v2/NoticeBannerV2/NoticeBannerV2"; import { ProjectPermissionSub, useProject, useProjectPermission, useSubscription } from "@app/context"; import { ProjectPermissionSecretRotationActions } from "@app/context/ProjectPermissionContext/types"; import { usePopUp } from "@app/hooks"; import { useDeleteSecretRotation, useGetSecretRotationProviders, useGetSecretRotations, useRestartSecretRotation } from "@app/hooks/api"; import { TSecretRotationProviderTemplate } from "@app/hooks/api/secretRotation/types"; import { CreateRotationForm } from "@app/pages/secret-manager/SecretRotationPage/components/CreateRotationForm"; const Page = () => { const { currentProject } = useProject(); const { permission } = useProjectPermission(); const navigate = useNavigate(); const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([ "createRotation", "activeBot", "deleteRotation", "upgradePlan", "secretRotationV2" ] as const); const workspaceId = currentProject?.id || ""; const canCreateRotation = permission.can( ProjectPermissionSecretRotationActions.Create, ProjectPermissionSub.SecretRotation ); const { subscription } = useSubscription(); const { data: secretRotationProviders, isPending: isRotationProviderLoading } = useGetSecretRotationProviders({ workspaceId }); const { data: secretRotations, isPending: isRotationLoading } = useGetSecretRotations({ workspaceId }); const { mutateAsync: deleteSecretRotation, variables: deleteSecretRotationVars, isPending: isDeletingRotation } = useDeleteSecretRotation(); const { mutateAsync: restartSecretRotation, variables: restartSecretRotationVar, isPending: isRestartingRotation } = useRestartSecretRotation(); const handleDeleteRotation = async () => { const { id } = popUp.deleteRotation.data as { id: string }; try { await deleteSecretRotation({ id, workspaceId }); handlePopUpClose("deleteRotation"); createNotification({ type: "success", text: "Successfully removed rotation" }); } catch (error) { console.log(error); createNotification({ type: "error", text: "Failed to remove rotation" }); } }; const handleRestartRotation = async (id: string) => { try { await restartSecretRotation({ id, workspaceId }); createNotification({ type: "success", text: "Secret rotation initiated" }); } catch (error) { console.log(error); createNotification({ type: "error", text: "Failed to restart rotation" }); } }; const handleCreateRotation = (provider: TSecretRotationProviderTemplate) => { if (subscription && !subscription?.secretRotation) { handlePopUpOpen("upgradePlan"); return; } if (!canCreateRotation) { createNotification({ type: "error", text: "Access permission denied!!" }); return; } handlePopUpOpen("createRotation", provider); }; return (
Documentation

Infisical is revamping its Secret Rotation experience.

PostgreSQL and Microsoft SQL Server Rotations can now be created from the{" "} Secret Manager Dashboard {" "} from the actions dropdown.

Rotated Secrets
{isRotationLoading && ( )} {!isRotationLoading && secretRotations?.length === 0 && ( )} {secretRotations?.map( ({ environment, secretPath, outputs, provider, id, lastRotatedAt, status, statusMessage }) => { const isDeleting = deleteSecretRotationVars?.id === id && isDeletingRotation; const isRestarting = restartSecretRotationVar?.id === id && isRestartingRotation; return ( ); } )}
Secret Name Environment Provider Status Last Rotation Action
{outputs .map(({ key }) => key) .join(",") .toUpperCase()}
{environment.slug}
{secretPath}
{provider}
{status} {status === "failed" && ( )}
{lastRotatedAt ? formatDistance(new Date(lastRotatedAt), new Date()) : "-"}
{(isAllowed) => ( handleRestartRotation(id)} > {isRestarting ? ( ) : ( )} )} {(isAllowed) => ( handlePopUpOpen("deleteRotation", { id })} > {isDeleting ? ( ) : ( )} )}
Infisical Rotation Providers
{isRotationProviderLoading && Array.from({ length: 12 }).map((_, index) => ( ))} {!isRotationProviderLoading && secretRotationProviders?.providers.map((provider) => (
{ if (evt.key !== "Enter") return; if (provider.isDeprecated) { handlePopUpOpen("secretRotationV2", provider.title); } else { handleCreateRotation(provider); } }} onClick={() => { if (provider.isDeprecated) { handlePopUpOpen("secretRotationV2", provider.title); } else { handleCreateRotation(provider); } }} > rotation provider logo
{provider.title}
))}
Request or create your own template
handlePopUpToggle("createRotation", isOpen)} provider={(popUp.createRotation.data as TSecretRotationProviderTemplate) || {}} /> handlePopUpToggle("deleteRotation", isOpen)} deleteKey="delete" onDeleteApproved={handleDeleteRotation} /> handlePopUpToggle("upgradePlan", isOpen)} text="You can add secret rotation if you switch to Infisical's Team plan." /> handlePopUpToggle("secretRotationV2", isOpen)} >

Infisical is revamping its Secret Rotation experience. Navigate to the{" "} Secret Manager Dashboard {" "} to create a {popUp.secretRotationV2.data} Rotation.

Secret Rotation V2 location
); }; export const SecretRotationPage = () => { const { t } = useTranslation(); return (
{t("common.head-title", { title: t("settings.project.title") })}
); };