001/*
002 * Copyright 2022-2026 Revetware LLC.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.soklet;
018
019import org.jspecify.annotations.NonNull;
020
021import javax.annotation.concurrent.ThreadSafe;
022import java.util.Optional;
023
024/**
025 * Thread-safe, server-owned control plane for the live MCP protection key
026 * ring.
027 * <p>
028 * Each server owns independent live state copied from its immutable
029 * {@link McpProtectionConfig}. Every successful mutation has one linearization
030 * point and validates against the live ring at that same point. Rejected
031 * mutations leave the ring unchanged.
032 * <p>
033 * A fleet rotation stages the identical key on every instance, compares the
034 * secret-free snapshots, activates the staged key everywhere, waits for the
035 * former key's maximum request-state lifetime and outstanding sealing
036 * reservations, and finally removes the former key from every instance.
037 * <p>
038 * A sealing reservation is Soklet's internal acquisition of one exact active
039 * key immediately before sealing. Activation may linearize while an earlier
040 * reservation finishes under the former key. Removal rejects a key until all
041 * of its reservations have drained.
042 *
043 * @author <a href="https://www.revetkn.com">Mark Allen</a>
044 */
045@ThreadSafe
046public interface McpProtectionKeyringManager {
047        /** @return this server's effective protection mode */
048        @NonNull
049        McpProtectionMode getProtectionMode();
050
051        /**
052         * Captures active and verification IDs plus their configuration fingerprint
053         * at one linearization point.
054         *
055         * @return secret-free snapshot, or empty when no production ring is active
056         */
057        @NonNull
058        Optional<@NonNull McpProtectionKeyringSnapshot> getKeyringSnapshot();
059
060        /**
061         * Adds a verification-only key to the live production ring.
062         * <p>
063         * Re-staging a byte-identical key under the same ID is an idempotent no-op,
064         * whether it is currently active or verification-only. The mutation rejects
065         * a duplicate ID with different material, material already present under a
066         * different ID, or material equal to the active trace-correlation key.
067         *
068         * @param verificationKey key to stage
069         * @throws IllegalArgumentException if the mutation is invalid
070         * @throws IllegalStateException if this server has no production keyring
071         */
072        void stageVerificationKey(@NonNull McpProtectionKey verificationKey);
073
074        /**
075         * Activates a staged key and atomically demotes the former active key.
076         * Activating the already-active ID is an idempotent no-op.
077         *
078         * @param keyId staged verification-key ID
079         * @throws IllegalArgumentException if the ID is unknown
080         * @throws IllegalStateException if this server has no production keyring
081         */
082        void activateStagedKey(@NonNull String keyId);
083
084        /**
085         * Atomically stages and activates a key.
086         * <p>
087         * This convenience is retry-safe for the byte-identical already-active key.
088         * Fleet deployments normally stage on every instance before activation.
089         *
090         * @param activeKey new active key
091         * @throws IllegalArgumentException if the mutation is invalid
092         * @throws IllegalStateException if this server has no production keyring
093         */
094        void rotateActiveKey(@NonNull McpProtectionKey activeKey);
095
096        /**
097         * Removes a verification-only key.
098         * <p>
099         * An absent ID returns {@code false}; removing the active ID is rejected. A
100         * key with an outstanding sealing reservation fails transiently with
101         * {@link McpProtectionKeyInUseException}.
102         *
103         * @param keyId verification-only key ID
104         * @return whether a key was removed
105         * @throws IllegalArgumentException if the ID names the active key
106         * @throws IllegalStateException if this server has no production keyring
107         * @throws McpProtectionKeyInUseException if the key is still used for sealing
108         */
109        @NonNull
110        Boolean removeVerificationKey(@NonNull String keyId);
111}