The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.
Follows the import procedure in README.md.
source/ the originating repository verbatim — 78 documents, 28 SVGs,
8 manifests, plus .source-revision recording the commit
final/ the SSOT
document.md 729 lines written from the 29 experiment documents, not
concatenated: what was predicted, what was measured, and
where the measurement itself was wrong
evidence/raw 125 outputs, flattened to <experiment>__<file> because
the originals collided (01-baseline.txt appeared three
times) and the audit only globs the top level
evidence/meta one per raw file; command and exitCode are null and the
README says why rather than inventing them
evidence/browser 22 captures
assets/ three diagrams through techviz
.techviz/ their VizSpecs
A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.
Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.
verify-pipeline.py passes. audit-records.py reports no issues.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
184 lines
18 KiB
Plaintext
184 lines
18 KiB
Plaintext
Sub-scope 06 (fileserver) payload operations · CSV export · testkit contracts · crash matrix
|
|
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
|
|
generatedAt=2026-08-30T01:30:13+00:00
|
|
|
|
=== OWNED FILES (3 main + 12 test) ===
|
|
d0566494c6024e6c068e980b7537200a0cf3844d main/LocalPersistentPayloadOperations.java 1101
|
|
140dca95e213bdc36c6a45a08a254904f9138c1d main/StreamingCsvEncoder.java 190
|
|
cb53573f65ec919834088b40343ad71352a8c754 main/FilesystemCsvExportAdapter.java 188
|
|
699b6db92aa73404f7319c5de7d1244354887286 test/LocalPersistentPayloadOperationsTest.java 757
|
|
2721049858a6857b49d089b87cd01677720d00ab test/FilesystemCsvExportAdapterTest.java 134
|
|
b8e0e713d8d87e39462ba229514e92c1c8195e6c test/FileserverCrashScenarioMain.java 473
|
|
819746893a4bc76be6b447a0ac9e19ad678ac24c test/testkit/ContentStoreContract.java 193
|
|
a0b7bac9cf0d04addfbe8b1700b2245509be6d8e test/testkit/CrashPoint.java 30
|
|
f7b14c62a7b19957859b23409991e8429916ca78 test/testkit/CrashRecoveryMatrixTest.java 173
|
|
c990e690ba089148cfcdc0dadeda05a6ca8617f9 test/testkit/LargeFileBoundedMemoryTest.java 152
|
|
1ad295c83d3cca34822b612e6cebb3857314f629 test/testkit/LocalContentStoreContractTest.java 42
|
|
2b44236a451ecc5f50f43badb8294787b58a95e6 test/testkit/MetadataPointerContentStoreContractTest.java 43
|
|
31f303c64278168dadc3fca71a51f38a51847cdb test/testkit/NfsAmbiguityIntegrationTest.java 42
|
|
3f1f7baf85d365968563052b6f1818b480ed678e test/testkit/NfsTestEnvironment.java 26
|
|
fdf5d1b0a0584dcc6eca86ada6be3298de15d462 test/testkit/PvcCertificationDescriptor.java 42
|
|
|
|
=== 8.1 reachability ===
|
|
$ git grep -rln 'LocalPersistentPayloadOperations\|StreamingCsvEncoder\|FilesystemCsvExportAdapter' -- . | grep -v "^adapter/outbound/fileserver/"
|
|
exit=1
|
|
|
|
$ grep -n 'class FilesystemCsvExportAdapter\|final class StreamingCsvEncoder\|final class LocalPersistentPayloadOperations' adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FilesystemCsvExportAdapter.java adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/StreamingCsvEncoder.java adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentPayloadOperations.java
|
|
adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FilesystemCsvExportAdapter.java:25:public class FilesystemCsvExportAdapter implements FileExportPort {
|
|
adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/StreamingCsvEncoder.java:22:final class StreamingCsvEncoder {
|
|
adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentPayloadOperations.java:42:final class LocalPersistentPayloadOperations {
|
|
exit=0
|
|
|
|
=== 8.2 payload operations: the residual pathname primitives, declared ===
|
|
$ grep -n 'the JDK.s missing relative' -B 3 -A 3 adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentPayloadOperations.java
|
|
35- * Secure payload boundary for the qualified local-persistent Fileserver R2 provider.
|
|
36- *
|
|
37- * <p>Caller values are converted to bounded generated segments before filesystem access. Reads,
|
|
38: * writes, and exact deletes use {@link SecureDirectoryStream}; the JDK's missing relative
|
|
39- * hard-link, directory-create, and directory-force primitives are bracketed by attested identity
|
|
40- * checks in this class.
|
|
41- */
|
|
exit=0
|
|
|
|
$ grep -n 'Files.createLink\|Files.createDirectory\|Files.newDirectoryStream\|Files.getFileStore\|Files.readAttributes\|Files.setPosixFilePermissions' adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentPayloadOperations.java
|
|
414: Files.readAttributes(directory, PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
|
|
789: Files.readAttributes(shardPath, PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
|
|
802: Files.createDirectory(
|
|
805: Files.setPosixFilePermissions(shardPath, PRIVATE_DIRECTORY_PERMISSIONS);
|
|
807: Files.readAttributes(shardPath, PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
|
|
813: return Files.getFileStore(path).name();
|
|
818: return Files.getFileStore(path).type();
|
|
934: Files.getFileStore(artifactPath).name(),
|
|
935: Files.getFileStore(artifactPath).type());
|
|
941: Files.createLink(link, existing);
|
|
1003: Files.getFileStore(path).name(),
|
|
1004: Files.getFileStore(path).type());
|
|
1013: DirectoryStream<Path> top = Files.newDirectoryStream(topDirectory);
|
|
exit=0
|
|
|
|
=== 8.2b CSV formula mitigation: which leading characters count ===
|
|
$ grep -n 'isFormulaCandidate' -A 10 adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/StreamingCsvEncoder.java
|
|
120: if (!isFormulaCandidate(value)) {
|
|
121- return value;
|
|
122- }
|
|
123- return switch (column.formulaPolicy()) {
|
|
124- case ALLOW -> value;
|
|
125- case MITIGATE -> {
|
|
126- formulaMitigated++;
|
|
127- yield "'" + value;
|
|
128- }
|
|
129- case REJECT ->
|
|
130- throw failure(
|
|
--
|
|
174: private static boolean isFormulaCandidate(String value) {
|
|
175- if (value.isEmpty()) {
|
|
176- return false;
|
|
177- }
|
|
178- return switch (value.charAt(0)) {
|
|
179- case '=', '+', '-', '@', '\t', '\r' -> true;
|
|
180- default -> false;
|
|
181- };
|
|
182- }
|
|
183-
|
|
184- private static FilePublicationException failure(
|
|
exit=0
|
|
|
|
$ grep -n 'formulaPolicy' -A 12 adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/StreamingCsvEncoder.java | head -18
|
|
123: return switch (column.formulaPolicy()) {
|
|
124- case ALLOW -> value;
|
|
125- case MITIGATE -> {
|
|
126- formulaMitigated++;
|
|
127- yield "'" + value;
|
|
128- }
|
|
129- case REJECT ->
|
|
130- throw failure(
|
|
131- FilePublicationException.Reason.INVALID_REQUEST,
|
|
132- "spreadsheet formula text rejected for column " + column.name());
|
|
133- };
|
|
134- }
|
|
135-
|
|
exit=0
|
|
|
|
=== 8.3 R1 legacy adapter vs R2 encoder: same job, declared difference ===
|
|
$ grep -n 'CREATE_NEW\|Files.write\|StringBuilder' adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FilesystemCsvExportAdapter.java | head
|
|
51: * to be assembled in one {@code StringBuilder} and then converted to a byte array, so peak memory
|
|
53: * And {@code Files.write} truncates, so exporting a name that already existed silently replaced
|
|
54: * whatever was there; {@code CREATE_NEW} makes a name collision an error the caller can see.
|
|
70: StandardOpenOption.CREATE_NEW,
|
|
exit=0
|
|
|
|
$ grep -n 'stand-in for NFS/SFTP' adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FilesystemCsvExportAdapter.java adapter/outbound/fileserver/README.md
|
|
adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FilesystemCsvExportAdapter.java:21: * (the "file server" boundary, a stand-in for NFS/SFTP). Fields are escaped per RFC-4180 and the
|
|
exit=0
|
|
|
|
=== 8.4 testkit: what the contracts and the crash matrix cover ===
|
|
$ grep -nE 'class |@Test| void |CrashPoint\.' adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashPoint.java adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java | head -40
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:45:class CrashRecoveryMatrixTest {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:54: @EnumSource(CrashPoint.class)
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:55: void aPublishedObjectIsAlwaysCompleteAndDigestMatched(CrashPoint crashPoint) throws Exception {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:63: assertThat(crashPoint).isNotEqualTo(CrashPoint.AFTER_METADATA_BEFORE_QUOTA);
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:75: @EnumSource(CrashPoint.class)
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:76: void aCrashNeverLeavesAPartialObjectUnderThePublishedKey(CrashPoint crashPoint) throws Exception {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:100: if (crashPoint == CrashPoint.AFTER_CREATE) {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:105: if (crashPoint == CrashPoint.DURING_APPEND) {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/CrashRecoveryMatrixTest.java:110: if (crashPoint == CrashPoint.AFTER_APPEND_COMMIT || crashPoint == CrashPoint.BEFORE_PUBLISH) {
|
|
exit=0
|
|
|
|
$ grep -nE 'class |@Test| void ' adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LocalContentStoreContractTest.java adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/MetadataPointerContentStoreContractTest.java | head -30
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:39:public abstract class ContentStoreContract {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:50: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:51: void createAppendFinalizeStatReadDeleteRoundTrip() throws Exception {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:61: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:62: void appendsAccumulateAndTheDigestCoversEveryByte() throws Exception {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:73: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:74: void aMismatchedOffsetIsRefusedWithoutMutatingTheObject() throws Exception {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:86: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:87: void aRangedReadReturnsExactlyTheRequestedBytes() throws Exception {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:95: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:96: void aDeclaredDigestThatDoesNotMatchIsRefusedAtFinalize() throws Exception {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:113: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:114: void aDeclaredLengthThatDoesNotMatchIsRefusedAtFinalize() throws Exception {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:128: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:129: void deletingAnAbsentObjectIsAnIdempotentSuccessThatReportsTheDivergence() throws Exception {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:137: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:138: void theStoreReportsCapabilitiesItActuallyProved() {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:143: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/ContentStoreContract.java:144: void twoUploadsNeverShareAPhysicalKey() throws Exception {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LocalContentStoreContractTest.java:18:class LocalContentStoreContractTest extends ContentStoreContract {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LocalContentStoreContractTest.java:25: void createStore() {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/MetadataPointerContentStoreContractTest.java:19:class MetadataPointerContentStoreContractTest extends ContentStoreContract {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/MetadataPointerContentStoreContractTest.java:26: void createStore() {
|
|
exit=0
|
|
|
|
$ grep -nE 'class |@Test| void |@EnabledIf|@Tag|Assumptions' adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsTestEnvironment.java adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/PvcCertificationDescriptor.java adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java | head -30
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:19:@EnabledIfEnvironmentVariable(named = NfsTestEnvironment.ENABLE_FLAG, matches = "true")
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:20:class NfsAmbiguityIntegrationTest {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:25: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:26: void aRenameWhoseAcknowledgementWasLostIsAmbiguousRatherThanRetryable() {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:31: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:32: void aStaleHandleAfterAServerRestartSendsTheFileToReconciliation() {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:37: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsAmbiguityIntegrationTest.java:38: void theEnvironmentGateIsExplicitAboutWhyItWouldSkip() {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/NfsTestEnvironment.java:11:public final class NfsTestEnvironment {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java:34:class LargeFileBoundedMemoryTest {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java:43: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java:44: void uploadingALargeObjectNeverRequestsMoreThanOneBufferAtATime() {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java:71: @Test
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java:72: void readingALargeObjectIsAlsoBounded() throws Exception {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/testkit/LargeFileBoundedMemoryTest.java:116: private static final class CountingSource implements ReadableByteChannel {
|
|
exit=0
|
|
|
|
$ git grep -n 'FileserverCrashScenarioMain' -- 'adapter/outbound/fileserver/src' | head
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/FileserverCrashScenarioMain.java:50:final class FileserverCrashScenarioMain {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/FileserverCrashScenarioMain.java:65: private FileserverCrashScenarioMain() {}
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:31: private static final String CHILD_MAIN = FileserverCrashScenarioMain.class.getName();
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:36: @EnumSource(FileserverCrashScenarioMain.CrashBoundary.class)
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:38: FileserverCrashScenarioMain.CrashBoundary boundary) throws Exception {
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:43: assertThat(crashed.exitCode()).isEqualTo(FileserverCrashScenarioMain.CRASH_EXIT_CODE);
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:49: ? FileserverCrashScenarioMain.RECOVERY_INDETERMINATE_QUARANTINED
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:50: : FileserverCrashScenarioMain.RECOVERY_SUCCESS;
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:69: awaitMarker(holder, FileserverCrashScenarioMain.LOCK_ACQUIRED);
|
|
adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/LocalPersistentCrashRecoveryTest.java:74: assertThat(excluded.output()).isEqualTo(FileserverCrashScenarioMain.LOCK_BUSY);
|
|
exit=0
|
|
|