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}