feat: add production capability foundations

This commit is contained in:
donghyeon-ka
2026-07-31 23:50:44 +09:00
parent b3add0162d
commit 567422f2e5
757 changed files with 132385 additions and 2146 deletions
@@ -1,12 +1,14 @@
# Fileserver Production Capability Deep Design
- 작성일: 2026-07-26
- 상태: 상세 설계 완료, Phase 01 및 Phase 2 일부 local R1 구현, R2 이상 미구현
- 상태: 상세 설계 완료, Phase 01 및 Phase 2 `local-persistent` R2 구현, 후속 provider/운영
capability 미구현
- 독립 아키텍처 재리뷰: blocker/high 0건
- 기준: Java 21, Spring Boot 4.0.0, Gradle 멀티모듈 Clean Architecture
- 대상 leaf: `adapter-outbound-fileserver`
- 구현 추적: 이 문서의 목표 전체가 아니라 framework-free port, local staged CSV, single-node
operation journal/recovery까지만 적용되었다.
- 구현 추적: 이 문서의 장기 목표 전체가 아니라 provider-neutral application/control 계약,
exact selector, pre-provisioned local filesystem을 위한 `local-persistent` R2 provider까지만
적용되었다.
- 상위 문서:
[Production Capability Platform Design](2026-07-26-production-capability-platform-design.md)
@@ -26,24 +28,45 @@
- operation-scoped JVM/OS file lock과 hard-link-only publication protocol;
- overwrite-capable legacy port의 별도 opt-in/root 및 canonical overlap 차단;
- 안전한 commit primitive가 없을 때 copy-to-final로 downgrade하지 않는 fail-closed 동작.
- `app.fileserver` exact destination/provider selector와 producer 호출 전 unknown destination
거부;
- provider ID별 singleton runtime과 서로 다른 provider ID의 동일 normalized root 소유 거부;
- provider-neutral canonical operation v2/private manifest/reference index와 opaque
`fsr1.<route-token>.<file-id>.<check-digits>` direct lookup;
- strict UTF-8/canonical schema-v1 terminal record의 read-only compatibility와 schema-v2-only
write;
- absolute/pre-provisioned root, ancestor/root symlink, real path, owner/mode, FileStore
name/type, mount sentinel, `SecureDirectoryStream`, exclusive-create/hard-link/file·directory
force startup attestation;
- `WRITING -> SEALED -> DATA_PUBLISHED -> MANIFEST_PUBLISHED -> REFERENCE_PUBLISHED ->
PUBLISHED` durable publication ordering;
- data/manifest/reference/receipt 전체 교차검증과 deterministic resume/quarantine;
- terminal mismatch에서 journal과 모든 artifact를 불변 보존하는 fail-closed recovery;
- `FILE_AND_DIRECTORY_SYNC` receipt와 forked-process force-boundary/OS operation-lock
qualification seam;
- `app-bootstrap` opt-in composition과 disabled-default/no-filesystem-side-effect gating.
아직 구현되지 않은 범위:
- Phase 2의 cross-node fencing, reference/private-manifest index, exhaustive crash/symlink-race
qualification;
- 운영 cleanup/quota/retention과 effective capability probe인 Phase 3;
- `shared-mounted`/NFS multi-client semantics와 cross-node producer fencing;
- 운영 background reconciliation/reaper, retention, quota/backpressure인 Phase 3;
- Fileserver 전용 readiness/health, metrics, tracing, structured audit;
- SFTP provider인 Phase 4;
- NFS/HA/bootstrap evidence인 Phase 5;
- NFS/HA/operator topology evidence인 Phase 5;
- optional delete/read/scan operation인 Phase 6.
따라서 현재 journal은 single-node local recovery seam이며 Fileserver R2 완료 증거가 아니다.
기존 `FileExportPort`도 호환성을 위해
따라서 현재 R2 claim은 `local-persistent`에만 한정한다. `FILE_AND_DIRECTORY_SYNC`는 attested
filesystem 안에서 file과 관련 directory force가 성공했다는 뜻이며 physical device,
storage-controller cache, volume replica, backup/site의 power-loss protection을 뜻하지 않는다.
그 축은 deployment/storage evidence가 별도로 소유한다. 기존 `FileExportPort`도 호환성을 위해
남아 있으며, 전체 행 materialization과 absolute path receipt를 사용하는 legacy 경로다.
기존 R1 terminal artifact는 strict read-only로 원래 `PROCESS_LOCAL_SYNC` receipt만 복원하고
manifest/reference 생성, schema-v2 rewrite, R2 guarantee 자동 승격을 하지 않는다.
## 1. 설계 판정
현재 Fileserver 구현은 운영 파일서버가 아니라 다음 한 경로만 제공하는 R1 이하의 로컬
CSV 예제다.
설계 시작 당시 Fileserver 구현은 운영 파일서버가 아니라 다음 한 경로만 제공하는 R1 이하의
로컬 CSV 예제였다. 현재의 increment 상태와 보장 경계는 §0을 따른다.
```text
List<List<String>>
@@ -109,7 +132,11 @@ List<List<String>>
이번 문서는 위 항목을 구현 계획을 작성할 수 있는 수준까지 확정한다.
## 3. 현재 코드의 증거 기반 진단
## 3. 초기 코드의 증거 기반 진단
아래 표는 설계가 시작된 2026-07-26의 baseline을 보존한 역사적 진단이다. 현재 구현 상태는
§0이 권위이며, 아래 결함 중 streaming/opaque receipt/exclusive publication/control plane/local
attestation/composition은 후속 increment에서 해소되었다.
| 영역 | 현재 구현 | 운영상 의미 |
| --- | --- | --- |
@@ -134,13 +161,14 @@ List<List<String>>
- `src/application-core/src/main/java/dev/caskeleton/application/fileexport/FileExportPort.java`
- `src/application-core/src/main/java/dev/caskeleton/application/fileexport/ExportedFile.java`
- `src/adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FilesystemCsvExportAdapter.java`
- `src/adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FileExportProperties.java`
- `src/adapter/outbound/fileserver/src/main/java/dev/caskeleton/adapter/outbound/fileserver/FileExportSettings.java`
- `src/adapter/outbound/fileserver/src/test/java/dev/caskeleton/adapter/outbound/fileserver/FilesystemCsvExportAdapterTest.java`
- `src/config/architecture/modules.json`
- `src/app-bootstrap/build.gradle`
현재 7개 fileserver unit test와 leaf `check`는 성공한다. 이는 현재 문서화된 로컬 happy-path
계약이 동작한다는 증거일 뿐 production readiness 증거는 아니다.
당시 7개 fileserver unit test와 leaf `check` 성공은 로컬 happy-path만 증명했다. 현재의
`local-persistent` claim은 별도 root attestation, control/payload/recovery, forked crash와
cross-process OS lock qualification suite의 통과를 요구한다.
## 4. 범위와 명시적 비범위
@@ -1927,22 +1955,26 @@ ca-skeleton:
`docs/registries/env-keys.yaml`, `application.yml`, typed settings, conditional beans를 end-to-end
검증한다.
Template baseline에 필요한 key:
현재 구현된 `local-persistent` composition에 등록하는 key:
```text
APP_FILESERVER_PRIMARY_ROOT
APP_FILESERVER_PRIMARY_MOUNT_ID
APP_FILESERVER_SFTP_HOST
APP_FILESERVER_SFTP_USERNAME
APP_FILESERVER_SFTP_PRIVATE_KEY_SECRET_REF
APP_FILESERVER_SFTP_KNOWN_HOSTS_SECRET_REF
APP_FILESERVER_SFTP_CONTROL_ROOT
APP_FILESERVER_SFTP_SPOOL_ROOT
APP_FILESERVER_SECRET_CONFIG_ROOT
APP_FILESERVER_ENABLED
APP_FILESERVER_LOCAL_ROOT
APP_FILESERVER_LOCAL_EXPECTED_FILE_STORE_NAME
APP_FILESERVER_LOCAL_EXPECTED_FILE_STORE_TYPE
APP_FILESERVER_LOCAL_MOUNT_SENTINEL_SHA256
APP_FILESERVER_LOCAL_EXPECTED_OWNER
```
모두 restart-only다. `APP_FILESERVER_ENABLED=false`가 shipped default이며, 나머지 다섯
attestation 값은 `app.fileserver.enabled=true`일 때 모두 필요하다. Root는 absolute/existing
directory, FileStore name/type과 owner는 non-blank exact match, sentinel digest는 64-character
lowercase SHA-256여야 한다.
Dynamic destination topology는 YAML/config tree가 소유하고 secret value는 secret source가
제공한다.
제공한다. 앞의 broader topology 예시에 있는 SFTP/NFS key는 아직 env registry나 shipped
`application.yml`에 등록하지 않는다. 실제 provider, dependency, real-service qualification이
추가되는 후속 increment에서만 등록한다.
## 24. Health와 observability
@@ -2228,9 +2260,10 @@ Nightly:
### 27.1 Dependency ownership
현재 가장 가까운 `src/adapter/outbound/fileserver/CLAUDE.md`는 pure JDK, external dependency
없음, NFS/SFTP stand-in만을 허용한다. 따라서 이 문서만으로 SFTP SDK를 Gradle에 추가할 수
없다. 구현 Phase 0에서 아키텍처 승인과 함께 다음 rule drift를 먼저 갱신한다.
현재 가장 가까운 `src/adapter/outbound/fileserver/CLAUDE.md`는 pure JDK filesystem과 Spring
configuration baseline만 허용하고 `local-persistent`만 구현 대상으로 인정한다. NFS/SFTP
stand-in이나 SDK는 허용하지 않는다. 따라서 이 문서만으로 SFTP SDK를 Gradle에 추가할 수 없다.
후속 SFTP 구현에서는 아키텍처 승인과 함께 다음 rule drift를 먼저 갱신한다.
- local `CLAUDE.md`의 책임을 local-only demo에서 provider-based publication으로 변경;
- external `NONE` 규칙을 exact allowlist로 변경;
@@ -2240,7 +2273,7 @@ Nightly:
이 rule migration 전 SFTP dependency 추가나 runtime activation은 HARD-STOP이다.
`adapter-outbound-fileserver`:
후속 provider rule migration의 후보 allowlist이며 현재 dependency가 아니다:
- JDK NIO local/mounted provider;
- Spring autoconfigure;
@@ -2260,7 +2293,8 @@ starter를 추가하지 않는다.
### 27.2 Bootstrap composition
안전한 explicit binding/gating과 config test가 먼저 구현된 후:
`local-persistent`에 대한 안전한 explicit binding/gating과 config test가 구현되었고 다음
composition을 적용했다.
1. `modules.json`의 `app-bootstrap.allowed_dependencies`에
`adapter-outbound-fileserver` 추가;
@@ -2271,7 +2305,8 @@ starter를 추가하지 않는다.
6. disabled-adapter architecture scan에 fileserver 추가;
7. env/settings/readiness contract 추가.
Classpath에 들어왔다는 이유로 local provider가 활성화되면 안 된다.
`application.yml`의 `app.fileserver.enabled=false`가 shipped default다. Classpath에 들어왔다는
이유만으로 local provider가 활성화되거나 filesystem side effect가 발생하지 않는다.
### 27.3 SDK split trigger
@@ -2287,9 +2322,11 @@ Classpath에 들어왔다는 이유로 local provider가 활성화되면 안 된
### Phase 0 — Truthful topology와 contract freeze
- 현재 Fileserver를 R1 local CSV demo로 명시;
상태: 완료. 현재 문서는 provider별 구현 상태와 보장 경계를 분리한다.
- 초기 Fileserver를 R1 local CSV demo로 명시하고 후속 R2 범위를 분리;
- Fileserver `CLAUDE.md`와 README의 responsibility/dependency/registry SSOT drift 수정;
- current bootstrap 미합성 상태 명시;
- 초기 bootstrap 미합성 상태와 후속 disabled-default opt-in composition을 함께 기록;
- v2 contract와 error registry 승인;
- journal/reference/control-plane schema 승인;
- accepted-attempt와 global coordination guarantee 분리;
@@ -2306,6 +2343,8 @@ Acceptance:
### Phase 1 — Streaming application contract와 CSV
상태: 완료. Framework-free `FilePublicationPort`와 bounded streaming CSV 경로가 구현되었다.
- `FilePublicationPort`;
- operation ID/fingerprint;
- effective policy snapshot;
@@ -2321,6 +2360,9 @@ Acceptance:
### Phase 2 — Secure local/mounted publication
상태: `local-persistent` 완료. `shared-mounted`/NFS multi-client profile과 cross-node fencing은
미구현이다.
- staging;
- digest/manifest;
- sealed journal과 protocol별 artifact ordering;
@@ -2336,6 +2378,8 @@ Acceptance:
### Phase 3 — Resource/maintenance/observability
상태: 미구현.
- concurrency/byte quota;
- timeout/cancel/shutdown;
- staging reaper/report;
@@ -2348,6 +2392,8 @@ Acceptance:
### Phase 4 — SFTP provider
상태: 미구현. SFTP setting/env/dependency/bean도 등록하지 않는다.
- Spring Integration/Apache MINA;
- host key/secrets;
- bounded pool/timeouts;
@@ -2362,6 +2408,9 @@ Acceptance:
### Phase 5 — NFS/HA evidence와 bootstrap
상태: `app-bootstrap`의 disabled-default opt-in composition과 local env mapping만 완료.
NFS/HA/operator topology evidence는 미구현이다.
- multi-client NFS profile;
- operator attestation;
- app-bootstrap composition;
@@ -2374,6 +2423,8 @@ Acceptance:
### Phase 6 — Optional read/delete와 module split review
상태: 미구현.
- opaque content transfer;
- expected-version managed delete;
- provider split 조건 재평가;
@@ -2381,6 +2432,9 @@ Acceptance:
## 29. 완료 기준
아래는 이 장기 설계 전체의 완료 기준이며 현재 충족되지 않았다. 현재 완료 claim은 §0의
`local-persistent` R2 범위로 제한한다.
Fileserver R2 완료를 주장하려면:
- application contract에 path/provider/SDK가 없음;