chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,638 @@
|
||||
# VD-16: Browser transfer composition과 Image delivery
|
||||
|
||||
- 상태: Accepted — design complete, implementation pending
|
||||
- 결정일: 2026-07-28
|
||||
- 이 ADR이 선택한 common delta의 current status:
|
||||
`DESIGNED_NOT_IMPLEMENTED`
|
||||
- common delta의 목표 reference status:
|
||||
`AVAILABLE_NOT_COMPOSED`
|
||||
- 관련 결정: VD-10, VD-11, VD-12, VD-13, VD-14, VD-15
|
||||
- current status ledger:
|
||||
`docs/architecture/browser-data-capability-completion-ledger.md`
|
||||
- 재검토: 첫 product upload/download/Image CDN capability를 조합하기 전
|
||||
|
||||
## 배경
|
||||
|
||||
현재 저장소에는 File runtime, presigned capability provider/vault/executor,
|
||||
multipart/resumable upload, one-shot streaming download와 Image CDN verification
|
||||
runtime의 개별 factory가 있다. 이 구현들은 production bootstrap에서 제거돼
|
||||
있고 각각의 local `close()` 또는 `dispose()`만 제공한다.
|
||||
|
||||
제품에서 이들을 직접 조합하면 다음 문제가 생긴다.
|
||||
|
||||
- account/session이 바뀌어도 이전 capability, checkpoint, refresh 또는 async
|
||||
completion이 살아남을 수 있다.
|
||||
- 서로 다른 config가 같은 byte/resource/preset을 다르게 해석할 수 있다.
|
||||
- 일부 provider만 생성된 partial runtime이 요청을 받기 시작할 수 있다.
|
||||
- upload, download와 image에 retry, kill switch, deadline과 observation owner가
|
||||
중복될 수 있다.
|
||||
- Image engine은 이미 decode된 server descriptor를 받으므로 BFF transport,
|
||||
expiry refresh와 presentation handoff의 책임이 비어 있다.
|
||||
- 실제 provider가 frontend mock과 같은 계약을 지키는지 재사용 가능한
|
||||
conformance harness가 없다.
|
||||
|
||||
이 결정은 도메인별 upload 화면이나 cloud vendor를 공통 플랫폼에 넣지 않는다.
|
||||
선택된 capability를 안전하게 조립·폐기하는 composition protocol과 Image
|
||||
descriptor acquisition 경계를 정한다.
|
||||
|
||||
## 현재 상태
|
||||
|
||||
| 항목 | 상태 | 설명 |
|
||||
| --- | --- | --- |
|
||||
| 개별 presigned/upload/image runtime | `AVAILABLE_NOT_COMPOSED` | factory와 deterministic test가 있으나 제품 graph에는 없음 |
|
||||
| one-shot streaming download | `AVAILABLE_NOT_COMPOSED` | Range resume가 아닌 전체 객체 스트림 |
|
||||
| top-level transfer runtime | `DESIGNED_NOT_IMPLEMENTED` | export index만 있고 atomic factory/readiness/lifecycle 없음 |
|
||||
| Image descriptor HTTP provider | `DESIGNED_NOT_IMPLEMENTED` | caller가 `BackendIssuedImageAsset`을 직접 전달 |
|
||||
| safe image DOM projection | `DESIGNED_NOT_IMPLEMENTED` | presentation descriptor는 있으나 renderer boundary 없음 |
|
||||
| app-managed background download | `NOT_SELECTED` | VD-14의 별도 optional capability |
|
||||
| cross-browser background-download guarantee | `PLATFORM_LIMITED` | browser-managed handoff가 기본 fallback |
|
||||
| app-managed background upload | `NOT_SELECTED` | durable source staging/worker auth protocol이 별도로 필요 |
|
||||
| cross-browser background-upload guarantee | `PLATFORM_LIMITED` | worker lifetime/local source permission을 공통 보장할 수 없음 |
|
||||
|
||||
이 ADR을 추가해도 위 상태는 자동으로 바뀌지 않는다. port, runtime, test와
|
||||
removal evidence가 구현된 뒤에만 reference 상태를 올린다.
|
||||
|
||||
## 결정
|
||||
|
||||
### 1. 하나의 account-scoped composition owner
|
||||
|
||||
선택된 file/transfer/image capability는
|
||||
`BrowserTransferRuntimeComposition` 역할의 단일 owner가 다음 순서로 생성한다.
|
||||
|
||||
```text
|
||||
parse immutable config
|
||||
-> validate implementation ceilings
|
||||
-> obtain immutable session/account scope
|
||||
-> create policy registries
|
||||
-> create provider transports
|
||||
-> create capability vaults
|
||||
-> create checkpoint/lock/channel owners
|
||||
-> create file/upload/download/image runtimes
|
||||
-> run required compatibility probes
|
||||
-> publish READY facade atomically
|
||||
```
|
||||
|
||||
factory가 중간에 실패하면 생성된 owner를 역순으로 닫고 facade를 반환하지 않는다.
|
||||
partial runtime, degraded provider 또는 mutable config를 application에 노출하지
|
||||
않는다.
|
||||
|
||||
composition은 다음 두 종류를 반환하는 union이어야 한다.
|
||||
|
||||
```text
|
||||
READY {
|
||||
generation,
|
||||
capabilities,
|
||||
application facades,
|
||||
readiness,
|
||||
close()
|
||||
}
|
||||
|
||||
UNAVAILABLE {
|
||||
safe reason,
|
||||
retryability,
|
||||
fallback capability,
|
||||
disposePartial()
|
||||
}
|
||||
```
|
||||
|
||||
`UNAVAILABLE`에 provider URL, raw browser exception, account/tenant ID 또는
|
||||
credential을 넣지 않는다.
|
||||
|
||||
### 2. Runtime config는 closed schema다
|
||||
|
||||
config는 composition root만 읽고 깊은 snapshot/freeze한다. 최소한 다음
|
||||
registry-owned reference를 갖는다.
|
||||
|
||||
- config schema version과 runtime compatibility version
|
||||
- opaque session/account scope와 generation
|
||||
- application origin과 fixed BFF endpoint IDs
|
||||
- exact `PRESIGNED_TRANSFER_V1`, `PRESIGNED_MULTIPART_V1`,
|
||||
`RANGE_RESUMABLE_DOWNLOAD_V1`, `IMAGE_CDN_DESCRIPTOR_V1` 중 선택한 protocol
|
||||
registry와 fixed endpoint map
|
||||
- upload purpose/profile, part/concurrency/retry/deadline hard ceiling
|
||||
- download profile, size/integrity/strategy와 Range capability selection
|
||||
- checkpoint namespace, retention, inventory와 maintenance budget
|
||||
- lock/cancel transport selection과 unsupported outcome
|
||||
- Image issuer/origin/preset/key/probe/decode policy
|
||||
- descriptor refresh lead time, request/decode deadline와 concurrency
|
||||
- capability별 traffic admission과 kill switch
|
||||
- observation sink와 redaction policy
|
||||
- active-operation drain deadline
|
||||
|
||||
caller는 raw endpoint, URL, header, object key, transform, retry count, byte ceiling,
|
||||
cache policy 또는 account partition을 request마다 override할 수 없다.
|
||||
|
||||
config는 구현 절대 상한을 높일 수 없다. 구현 상한보다 큰 값, 중복 registry ID,
|
||||
same-origin private Image CDN, 모순되는 fallback 또는 provider 누락은 startup에서
|
||||
fail-closed한다.
|
||||
|
||||
### 3. Lifecycle state machine
|
||||
|
||||
top-level runtime은 다음 상태만 가진다.
|
||||
|
||||
```text
|
||||
CREATING
|
||||
-> PROBING
|
||||
-> READY
|
||||
-> DRAINING
|
||||
-> CLOSED
|
||||
|
||||
CREATING | PROBING
|
||||
-> FAILED
|
||||
-> CLOSED
|
||||
```
|
||||
|
||||
- `READY`만 새 operation을 받는다.
|
||||
- `DRAINING`은 새 operation을 거절하고 진행 중 operation에 bounded deadline을
|
||||
제공한다.
|
||||
- deadline 뒤 남은 operation은 runtime lifetime signal로 abort한다.
|
||||
- `close()`는 terminal/idempotent이며 `DRAINING/CLOSED`에서 반복 호출해도
|
||||
새 side effect를 만들지 않는다.
|
||||
- 닫힌 runtime은 reopen하지 않는다. 새 config/scope에는 새 generation을 만든다.
|
||||
- operation은 시작할 때 runtime generation과 account scope snapshot을 얻고
|
||||
모든 async boundary와 terminal commit 전에 다시 확인한다.
|
||||
- 늦게 끝난 fetch, hash, IndexedDB transaction, image verification 또는 decode가
|
||||
old generation이면 결과를 폐기하고 native resource를 닫는다.
|
||||
|
||||
### 4. Logout과 account/tenant switch
|
||||
|
||||
session owner notification이 authority다. BroadcastChannel, storage event,
|
||||
capability expiry 또는 page unload를 logout authority로 사용하지 않는다.
|
||||
|
||||
```text
|
||||
session owner announces local revoke
|
||||
-> traffic admission CLOSED
|
||||
-> runtime generation FENCED
|
||||
-> reject new operations
|
||||
-> signal active reads/fetch/backoff/probes
|
||||
-> bounded drain
|
||||
-> close capability and image vaults
|
||||
-> close cancel channels and release locks/connections
|
||||
-> apply checkpoint retention/purge policy with exact old scope
|
||||
-> dispose observations
|
||||
-> CLOSED
|
||||
-> construct new scope/runtime independently
|
||||
```
|
||||
|
||||
- old-scope purge와 new-scope open을 같은 transaction이나 facade에 섞지 않는다.
|
||||
- checkpoint가 crash 때문에 남아도 exact scope/policy binding이 다르면 새
|
||||
runtime이 읽지 못해야 한다.
|
||||
- presigned URL, signed headers와 Image private URL은 어떤 teardown record에도
|
||||
저장하지 않는다.
|
||||
- old account의 descriptor refresh, part completion과 download destination
|
||||
commit은 generation fence 뒤 성공으로 보고하지 않는다.
|
||||
- logout이 backend capability의 즉시 revoke를 보장하지 않는다. 강한 회수가
|
||||
필요하면 BFF가 revoke authority 또는 proxy/relay를 제공해야 한다.
|
||||
|
||||
### 5. Capability별 facade
|
||||
|
||||
application에는 top-level runtime 자체나 native adapter를 반환하지 않는다.
|
||||
composition은 설치된 feature에 필요한 좁은 facade만 주입한다.
|
||||
|
||||
```text
|
||||
FeatureUploadFacade
|
||||
-> select local file profile
|
||||
-> create/resume/pause/abort approved purpose
|
||||
|
||||
FeatureDownloadFacade
|
||||
-> request approved resource
|
||||
-> receive policy-selected handoff/save outcome
|
||||
|
||||
FeatureImageFacade
|
||||
-> request opaque asset/preset
|
||||
-> receive safe presentation descriptor
|
||||
```
|
||||
|
||||
feature는 `File`, `Blob`, `Response`, `ReadableStream`, `FileSystemHandle`,
|
||||
presigned URL, Image signature DTO, checkpoint store 또는 QueryClient를 받지 않는다.
|
||||
progress UI를 위한 observation도 bounded aggregate snapshot이며 transfer
|
||||
authority가 아니다.
|
||||
|
||||
## Upload lifecycle integration
|
||||
|
||||
### 6. Pause와 abort는 다르다
|
||||
|
||||
top-level upload facade가 향후 제공할 상태는 다음과 같다.
|
||||
|
||||
```text
|
||||
ACTIVE
|
||||
-> PAUSE_REQUESTED
|
||||
-> PAUSED
|
||||
-> RESUMING
|
||||
-> ACTIVE
|
||||
|
||||
ACTIVE | PAUSED
|
||||
-> ABORT_REQUESTED
|
||||
-> ABORT_PENDING
|
||||
-> ABORTED
|
||||
|
||||
ACTIVE
|
||||
-> COMPLETING
|
||||
-> QUARANTINED
|
||||
```
|
||||
|
||||
- pause는 새 part와 retry를 중지하고 현재 bounded native operation을 abort한 뒤
|
||||
non-authorizing checkpoint를 유지한다.
|
||||
- cross-context pause wire literal은 `RESUMABLE_UPLOAD_PAUSE_V1`이며
|
||||
`uploadKey`, exact scope/generation과 bounded message metadata만 운반한다.
|
||||
- durable `PAUSED`를 추가하는 checkpoint는 `schemaVersion: 2`다. v1
|
||||
`ACTIVE | ABORT_PENDING` reader/writer와 섞지 않고 old-writer drain,
|
||||
historical migration과 N-1 fail-closed를 증명한다.
|
||||
- abort는 server session authority와 reconcile한 뒤 terminal checkpoint를
|
||||
제거한다.
|
||||
- pause signal은 authority가 아니라 best-effort same-scope hint다. 수신자는
|
||||
exact upload key/scope/generation을 검증한다.
|
||||
- checkpoint inventory는 presigned URL이나 raw file path 없이 opaque upload
|
||||
key, safe state, age/byte/part bucket과 expiry만 반환한다.
|
||||
- inventory/list와 retention sweep은 count, cursor, deadline을 갖는다.
|
||||
- abandoned/expired checkpoint는 server status 또는 expiry policy와 CAS를
|
||||
확인한 뒤 bounded batch로 제거한다.
|
||||
- file 재선택 뒤 source fingerprint/size/media/part layout이 exact하게 맞지
|
||||
않으면 resume하지 않는다.
|
||||
|
||||
app-managed background upload는 이 state machine을 재사용할 수 있지만 page
|
||||
runtime의 pause/resume를 background 보장으로 표현하지 않는다.
|
||||
|
||||
## Download integration
|
||||
|
||||
### 7. Strategy selector
|
||||
|
||||
download strategy는 application caller가 지정하지 않고 immutable profile,
|
||||
resource delivery class, expected bytes, browser capability와 user activation을
|
||||
입력으로 하는 headless selector가 결정한다.
|
||||
|
||||
| 조건 | 결과 |
|
||||
| --- | --- |
|
||||
| save picker 지원, user activation 있음, large stream | `WHOLE_OBJECT_PICKER_STREAM` |
|
||||
| server-managed resource, picker 없음 또는 handoff가 제품 정책 | `BROWSER_MANAGED_HANDOFF` |
|
||||
| generated artifact가 approved Blob cap 이하 | `BOUNDED_OBJECT_URL` |
|
||||
| large generated artifact, picker 없음 | `SERVER_GENERATION_REQUIRED` 또는 `UNSUPPORTED` |
|
||||
| Range profile + seekable destination + provider contract | `RANGE_RESUMABLE_FOREGROUND` |
|
||||
| background download가 선택되고 지원되는 browser + owned staging | `APP_MANAGED_BACKGROUND_DOWNLOAD` |
|
||||
|
||||
selector는 capability probe와 actual invocation failure를 구분한다.
|
||||
`WHOLE_OBJECT_PICKER_STREAM`인데 picker가 없으면 request validation 오류가 아니라
|
||||
`UNSUPPORTED` 또는 승인 fallback이어야 한다. Safari/WebView/private mode의
|
||||
fallback도 동일 표에서 결정하며 user agent 문자열만으로 기능을 가정하지 않는다.
|
||||
|
||||
selector의 위 값은 application-level `DownloadExecutionPlan`이다. 현재
|
||||
file-delivery primitive와의 mapping은 다음처럼 닫는다.
|
||||
|
||||
| execution plan | 현재 adapter mapping |
|
||||
| --- | --- |
|
||||
| `WHOLE_OBJECT_PICKER_STREAM` | `DownloadStrategy=PROMPT_AND_STREAM` |
|
||||
| `BROWSER_MANAGED_HANDOFF` | source kind `BROWSER_MANAGED_RESOURCE` + `DownloadStrategy=BROWSER_MANAGED` |
|
||||
| `BOUNDED_OBJECT_URL` | `DownloadStrategy=BOUNDED_OBJECT_URL` |
|
||||
| `RANGE_RESUMABLE_FOREGROUND` | VD-14의 별도 future port; 현재 adapter에 mapping 금지 |
|
||||
| `APP_MANAGED_BACKGROUND_DOWNLOAD` | 별도 optional worker/staging port |
|
||||
| `SERVER_GENERATION_REQUIRED` / `UNSUPPORTED` | browser delivery를 시작하지 않는 closed outcome |
|
||||
|
||||
Range resume의 checkpoint, validator와 seek/truncate 결정은 VD-14를 따른다.
|
||||
app-managed background download는 기본 selector 결과가 아니다.
|
||||
|
||||
## Image descriptor acquisition과 delivery
|
||||
|
||||
### 8. Wire protocol
|
||||
|
||||
private Image descriptor BFF 계약은 다음 literal을 사용한다.
|
||||
|
||||
```text
|
||||
IMAGE_CDN_DESCRIPTOR_V1
|
||||
```
|
||||
|
||||
request는 composition-owned fixed HTTPS endpoint를 호출하며 최소한 다음
|
||||
application-safe 입력만 허용한다.
|
||||
|
||||
- protocol
|
||||
- opaque asset reference
|
||||
- named preset reference 또는 preset family
|
||||
- intended presentation class
|
||||
- current runtime generation에 묶인 CSRF/session transport
|
||||
|
||||
caller는 CDN URL, source URL, origin, object key, width, height, DPR, quality, fit,
|
||||
format, cache header, signing key ID나 expiry를 제출하지 않는다.
|
||||
|
||||
response decoder는 content type, status, header/body byte cap, total deadline와
|
||||
closed JSON shape를 검증한다. response에는 최소한 다음이 binding된다.
|
||||
|
||||
- exact protocol과 issuer
|
||||
- opaque asset ID와 immutable revision
|
||||
- origin/preset binding IDs
|
||||
- static raster media와 intrinsic dimensions
|
||||
- allowed preset binding ID set
|
||||
- issued/expiry time
|
||||
- signature algorithm, key ID, canonical binding digest와 signature
|
||||
|
||||
unknown field 정책은 protocol version에서 고정한다. credential, backend stack,
|
||||
raw provider key 또는 arbitrary transform은 descriptor에 포함하지 않는다.
|
||||
|
||||
HTTP `200`만 descriptor success다. `401/403/404`의 외부 mapping은 existence
|
||||
hiding 정책에 따라 closed failure로 정규화하며 raw backend message를 버린다.
|
||||
redirect, opaque response, wrong content type, oversize, timeout와 malformed
|
||||
descriptor는 capability를 생성하지 않는다.
|
||||
|
||||
### 9. Provider와 verifier 경계
|
||||
|
||||
- BFF는 authorization, asset existence, quarantine/promotion state와 descriptor
|
||||
발급 authority를 소유한다.
|
||||
- verifier registry는 composition이 승인한 bounded old/new public key set만
|
||||
가진다.
|
||||
- client signature 검증은 BFF authorization의 대체가 아니라 response tamper와
|
||||
registry mismatch를 fail-closed하는 보조 경계다.
|
||||
- CDN은 asset revision과 preset binding ID로 exact transform candidate를
|
||||
재계산한다. signed query나 client 계산 width가 authority가 아니다.
|
||||
- private asset의 emergency revocation은 backend/CDN/BFF가 소유한다. client는
|
||||
runtime close와 short expiry로 exposure를 줄인다.
|
||||
|
||||
### 10. Refresh state machine
|
||||
|
||||
descriptor provider는 asset/preset/scope/generation별 bounded single-flight만
|
||||
허용한다.
|
||||
|
||||
```text
|
||||
ABSENT
|
||||
-> FETCHING
|
||||
-> VERIFIED
|
||||
-> FRESH
|
||||
-> REFRESH_DUE
|
||||
-> REFRESHING
|
||||
-> FRESH
|
||||
|
||||
FETCHING | REFRESHING
|
||||
-> TERMINAL_POLICY_FAILURE
|
||||
-> PLACEHOLDER
|
||||
|
||||
FETCHING | REFRESHING
|
||||
-> RETRYABLE_FAILURE
|
||||
-> EXISTING_FRESH_UNTIL_EXPIRY | PLACEHOLDER
|
||||
|
||||
any state + scope/runtime revoke
|
||||
-> REVOKED
|
||||
```
|
||||
|
||||
- refresh lead time은 config가 정하되 expiry hard ceiling을 넘지 않는다.
|
||||
- private descriptor를 generic Query cache, Web Storage 또는 IndexedDB에
|
||||
persistence하지 않는다.
|
||||
- concurrent callers는 같은 verified result를 받을 수 있지만 URL/string을
|
||||
application state에 장기 복사하지 않는다.
|
||||
- 기존 descriptor가 아직 fresh하고 refresh가 일시 실패하면 expiry까지만
|
||||
사용할 수 있다. expiry 뒤 stale-while-error를 금지한다.
|
||||
- `lazy` load로 실제 fetch가 expiry 뒤 시작될 가능성이 있으면 eager/priority로
|
||||
바꾸거나 load 직전에 새 descriptor를 발급한다.
|
||||
- logout, key registry replacement와 runtime generation 변경은 in-flight
|
||||
transport, verification과 probe를 abort하고 늦은 결과를 폐기한다.
|
||||
|
||||
### 11. Safe presentation projection
|
||||
|
||||
공통 presentation primitive는 검증된 `ImagePresentationDescriptor`를 다음
|
||||
정적 속성으로만 투영한다.
|
||||
|
||||
- fallback `src`
|
||||
- ordered `<source type srcset>`
|
||||
- registry-owned `sizes`
|
||||
- intrinsic `width`와 `height`
|
||||
- `loading`, `decoding`, `fetchpriority`
|
||||
- `referrerpolicy`
|
||||
- `crossorigin="anonymous"`
|
||||
|
||||
primitive는 URL을 parse·조립·append하거나 transform query를 생성하지 않는다.
|
||||
descriptor가 가진 string을 React property로 전달하기 전에 closed allowed
|
||||
protocol/origin과 runtime generation을 다시 확인한다. raw HTML 주입과 CSS URL
|
||||
조립을 금지한다.
|
||||
|
||||
다음은 제품 presentation owner가 결정한다.
|
||||
|
||||
- 의미 있는 `alt`
|
||||
- placeholder와 오류 copy
|
||||
- skeleton/aspect-ratio UX
|
||||
- above-the-fold preload/priority
|
||||
- route/SSR preload hint
|
||||
- click/open/download behavior
|
||||
|
||||
descriptor refresh 실패를 native broken-image UI에만 맡기지 않고 제품이 승인한
|
||||
placeholder outcome으로 매핑한다.
|
||||
|
||||
## Provider contract harness
|
||||
|
||||
### 12. 재사용 가능한 suite
|
||||
|
||||
frontend는 transport 구현과 분리된 provider contract harness를 제공한다. 같은
|
||||
case set을 deterministic fake, local emulator와 실제 BFF/object storage/CDN에
|
||||
실행한다.
|
||||
|
||||
Presigned/download case:
|
||||
|
||||
- exact protocol/status/content type/body cap
|
||||
- method/origin/path/query/header binding
|
||||
- redirect와 credential omission
|
||||
- expiry/revocation/replay
|
||||
- truncation/overrun/content encoding
|
||||
- CORS exposed receipt/checksum
|
||||
|
||||
Multipart case:
|
||||
|
||||
- create/status/part/complete/abort idempotency
|
||||
- part layout/checksum/receipt reconciliation
|
||||
- 404/410/expiry와 orphan cleanup
|
||||
- quarantine/promotion
|
||||
- retry-after와 ambiguous completion
|
||||
|
||||
Image case:
|
||||
|
||||
- protocol/issuer/key/preset exact match
|
||||
- old/new signing key overlap과 removal
|
||||
- immutable revision/cache key
|
||||
- private no-store/CORS/CSP
|
||||
- pixel/decode/encoded byte ceiling
|
||||
- malformed/animated/active content
|
||||
- expiry refresh, revocation과 placeholder
|
||||
|
||||
actual provider test는 bearer URL, signature, account/asset/session ID를 artifact에
|
||||
기록하지 않는다. fixture는 synthetic opaque values와 disposable storage를 쓴다.
|
||||
|
||||
## Failure, readiness와 fallback
|
||||
|
||||
### 13. Readiness report
|
||||
|
||||
readiness는 application-safe capability별 결과다.
|
||||
|
||||
| 상태 | 의미 |
|
||||
| --- | --- |
|
||||
| `READY` | 필수 provider/config/browser probe가 모두 유효 |
|
||||
| `DEGRADED` | 승인된 좁은 fallback만 가능 |
|
||||
| `UNAVAILABLE` | 기능을 노출하지 않음 |
|
||||
| `DRAINING` | 기존 작업만 정리 중 |
|
||||
| `CLOSED` | terminal |
|
||||
|
||||
`DEGRADED`는 byte/pixel/security ceiling을 낮출 수는 있지만 높이지 않는다.
|
||||
예를 들어 enhanced picker off → native input, multipart concurrency off →
|
||||
sequential, private Image CDN off → approved placeholder는 가능하다.
|
||||
integrity off, arbitrary URL 허용, private response cache 또는 unbounded Blob은
|
||||
fallback이 아니다.
|
||||
|
||||
### 14. Kill switch
|
||||
|
||||
최소한 다음 switch를 독립적으로 둔다.
|
||||
|
||||
- new presigned issuance
|
||||
- direct object-storage data plane
|
||||
- new upload session
|
||||
- upload resume
|
||||
- upload complete
|
||||
- Range resume
|
||||
- picker streaming save
|
||||
- private image descriptor issuance
|
||||
- advanced image format
|
||||
- responsive candidates
|
||||
|
||||
switch 변경은 active operation의 의미를 소급 변경하지 않는다. 신규 진입을
|
||||
닫은 뒤 reconcile/drain한다. remote runtime config를 사용한다면 config의
|
||||
authenticity, release compatibility와 last-known-safe 정책을 별도 hosting 계약으로
|
||||
검증한다.
|
||||
|
||||
## 관측성과 개인정보
|
||||
|
||||
허용:
|
||||
|
||||
- operation kind와 safe outcome
|
||||
- runtime/readiness state
|
||||
- byte/part/candidate/retry/age/deadline bucket
|
||||
- policy rejection, abort, reconcile와 drain bucket
|
||||
- aggregate active count, checkpoint count와 orphan age
|
||||
|
||||
금지:
|
||||
|
||||
- URL, query, signed/request/response header
|
||||
- capability, bearer token, signature와 key material
|
||||
- resource/session/asset/upload/account/tenant ID
|
||||
- file name, local path, object key와 storage physical key
|
||||
- digest, ETag, receipt와 checkpoint payload
|
||||
- raw backend/browser exception message와 stack
|
||||
|
||||
runtime generation과 registry ID도 외부 telemetry에 그대로 보내지 않고 bounded
|
||||
compatibility bucket으로 변환한다.
|
||||
|
||||
## Rollout과 rollback
|
||||
|
||||
### 15. Rollout
|
||||
|
||||
1. ledger와 관련 ADR을 accepted로 고정한다.
|
||||
2. closed config/port와 provider contract harness를 먼저 구현한다.
|
||||
3. fake와 negative fixture에서 partial composition/late result를 거절한다.
|
||||
4. top-level runtime을 `AVAILABLE_NOT_COMPOSED`로 유지하고 removal gate를 만든다.
|
||||
5. product owner, exact scope와 provider config를 선택하고 bootstrap에 kill
|
||||
switch `DISABLED` 상태로 조합한다. 이 시점 primary status는 `COMPOSED`다.
|
||||
6. 실제 BFF/storage/CDN conformance와 readiness probe를 통과한다.
|
||||
7. Chromium/Firefox/WebKit과 실제 device에서 account switch, expiry와 crash를
|
||||
검증한다.
|
||||
8. runbook/rollback drill 뒤 internal cohort의 read-only/image public 또는 upload
|
||||
shadow flow부터 연다.
|
||||
9. private/image upload/download capability를 독립 canary와 kill switch로 확대한다.
|
||||
|
||||
### 16. Rollback
|
||||
|
||||
1. 신규 issuance/session/descriptor를 중지한다.
|
||||
2. runtime을 `DRAINING`으로 바꾸고 bounded operation을 마무리한다.
|
||||
3. ambiguous upload는 server reconcile하고 Range partial은 checkpoint 정책대로
|
||||
보존 또는 삭제한다.
|
||||
4. private capability를 backend에서 revoke하고 client runtime을 close한다.
|
||||
5. old compatible composition을 새 generation으로 다시 생성하거나 기능을
|
||||
unavailable로 유지한다.
|
||||
6. optional source, config와 facade를 제거하고 production module inventory와
|
||||
removal gate를 재검증한다.
|
||||
|
||||
schema version을 내리거나 checkpoint를 무조건 삭제해 rollback하지 않는다.
|
||||
|
||||
## 검증과 완료 기준
|
||||
|
||||
### 17. Deterministic
|
||||
|
||||
- partial factory failure의 reverse-order cleanup
|
||||
- close idempotency와 closed-runtime rejection
|
||||
- account switch 중 late fetch/hash/transaction/decode drop
|
||||
- config duplicate/ceiling/missing provider fail-closed
|
||||
- selector의 picker/size/resource matrix
|
||||
- upload pause/abort/reconcile race와 bounded inventory
|
||||
- descriptor refresh single-flight, expiry와 generation fence
|
||||
- picture projection의 arbitrary URL/query 생성 0건
|
||||
- diagnostics forbidden-value negative fixtures
|
||||
|
||||
### 18. Native browser
|
||||
|
||||
- Chromium/Firefox/WebKit의 native input/save picker fallback
|
||||
- multi-tab upload pause/cancel과 unsupported Web Locks path
|
||||
- page reload/account switch 중 active transfer drain
|
||||
- public/private Image fetch, actual CORS/no-store와 bitmap decode
|
||||
- offline/timeout/abort/late completion cleanup
|
||||
- Safari/WebView/private mode의 approved selector result
|
||||
|
||||
### 19. Provider와 operations
|
||||
|
||||
- fake/emulator/실제 BFF·object storage·CDN 동일 contract suite
|
||||
- signing key rotation과 emergency revoke drill
|
||||
- checkpoint retention/orphan cleanup drill
|
||||
- capability별 kill switch와 N-1 rollback
|
||||
- runtime removal 후 source-module inventory 0건
|
||||
|
||||
다음 조건 전에는 목표 상태를 `AVAILABLE_NOT_COMPOSED`로 올리지 않는다.
|
||||
|
||||
- [ ] top-level closed config와 atomic factory가 구현됐다.
|
||||
- [ ] generation-bound lifecycle과 account teardown이 구현됐다.
|
||||
- [ ] `RESUMABLE_UPLOAD_PAUSE_V1`, checkpoint schema v2 old-writer
|
||||
migration과 bounded upload inventory/retention owner가 구현됐다.
|
||||
- [ ] `PRESIGNED_TRANSFER_V1`과 선택 protocol의 strict codec, unknown-version
|
||||
rejection 및 reusable provider contract harness가 구현됐다.
|
||||
- [ ] strategy selector가 browser fallback을 fail-closed한다.
|
||||
- [ ] Image descriptor provider/refresh와 safe projection이 구현됐다.
|
||||
- [ ] deterministic fault, boundary, removal test가 통과한다.
|
||||
|
||||
다음 조건 전에는 제품 상태를 `COMPOSED`로 올리지 않는다.
|
||||
|
||||
- [ ] product owner와 opaque account scope가 정해졌다.
|
||||
- [ ] strict config를 사용하는 top-level runtime이 production bootstrap에서
|
||||
생성되고 실제 feature facade consumer까지 연결됐다.
|
||||
- [ ] traffic 기본값이 `DISABLED`이며 local teardown/close 경로가 연결됐다.
|
||||
|
||||
다음 조건 전에는 product-local production traffic을 승인하지 않는다.
|
||||
|
||||
- [ ] actual BFF/storage/CDN contract harness가 통과한다.
|
||||
- [ ] browser/device evidence와 runbook drill이 보존됐다.
|
||||
- [ ] kill switch와 rollback owner가 운영 승인됐다.
|
||||
|
||||
## 관련 문서
|
||||
|
||||
- [Browser data capability completion ledger](../browser-data-capability-completion-ledger.md)
|
||||
- [Presigned transfer and Image CDN](../presigned-transfer-and-image-cdn.md)
|
||||
- [VD-14 Resumable download와 background download](./VD-14-resumable-download-and-background-transfer.md)
|
||||
- [Server file capability infrastructure](../server-file-capability-infrastructure.md)
|
||||
- [Browser transfer recovery](../../operations/browser-transfer-recovery.md)
|
||||
|
||||
## 선택하지 않은 대안
|
||||
|
||||
- feature가 개별 transfer adapter factory를 직접 조합
|
||||
- singleton runtime을 여러 account/tenant가 공유
|
||||
- raw presigned/Image URL을 Query cache나 persistence에 저장
|
||||
- Image descriptor endpoint/transform을 request마다 caller가 지정
|
||||
- page abort를 upload pause 또는 server abort 완료로 간주
|
||||
- browser-managed handoff를 저장 완료로 간주
|
||||
- user-agent 문자열만으로 Safari fallback 결정
|
||||
- Service Worker를 설치하면 background upload/download가 보장된다고 가정
|
||||
- capability 일부만 준비된 partial runtime을 degraded success로 반환
|
||||
|
||||
## 결과
|
||||
|
||||
장점:
|
||||
|
||||
- account 전환과 teardown의 한 owner가 생긴다.
|
||||
- 개별 runtime의 안전한 메커니즘을 제품별 facade로 좁혀 조합할 수 있다.
|
||||
- provider mock과 실제 인프라 사이의 계약 차이를 같은 suite로 찾을 수 있다.
|
||||
- Image URL과 transform이 application/presentation에서 재조립되지 않는다.
|
||||
- capability별 rollout, kill switch와 제거가 독립적이다.
|
||||
|
||||
비용:
|
||||
|
||||
- config, lifecycle, provider harness와 browser evidence가 늘어난다.
|
||||
- 제품이 선택하지 않은 capability는 여전히 조합할 수 없으며 이것이 의도된
|
||||
결과다.
|
||||
- actual provider와 운영 증거 없이는 reference runtime 구현만으로 production
|
||||
완료를 주장할 수 없다.
|
||||
Reference in New Issue
Block a user