feat: 기능 추가 과정중
This commit is contained in:
@@ -0,0 +1,898 @@
|
||||
# VD-14: Resumable download와 background download 경계
|
||||
|
||||
- 상태: Accepted — production design complete, implementation pending
|
||||
- 결정일: 2026-07-28
|
||||
- catalog recipe availability: `RECIPE_AVAILABLE` (primary status/selection과 별도)
|
||||
- Range resumable download primary status: `DESIGNED_NOT_IMPLEMENTED`
|
||||
- app-managed background download primary status: `NOT_SELECTED`
|
||||
- cross-browser app-managed background download guarantee: `PLATFORM_LIMITED`
|
||||
- 관련 결정: VD-10, VD-11, VD-12
|
||||
- current status ledger:
|
||||
`docs/architecture/browser-data-capability-completion-ledger.md`
|
||||
- 재검토: 제품이 Range 재개, 탭 종료 뒤 전달 또는 대용량 Safari fallback을
|
||||
선택할 때
|
||||
|
||||
## 1. 배경과 현재 사실
|
||||
|
||||
현재 reference runtime은 whole-object `200` response를 bounded stream으로 읽어
|
||||
foreground destination에 저장하거나 browser download manager에 handoff한다. 이
|
||||
경로는 전체 payload를 하나의 `Blob`으로 만들지 않고 byte length와 SHA-256을
|
||||
검증하지만, 네트워크나 탭이 중단되면 다음 실행은 byte 0부터 다시 시작한다.
|
||||
|
||||
Range resume는 기존 stream에 `Range` header 하나를 추가하는 기능이 아니다.
|
||||
representation identity, exact `206 Content-Range`, durable partial destination,
|
||||
checkpoint CAS, `200/412/416` reconciliation과 마지막 whole-object integrity가
|
||||
하나의 protocol이어야 한다. background download도 Range resume와 동일하지 않다.
|
||||
브라우저 download manager에 넘기는 것과 애플리케이션이 Service Worker에서
|
||||
전송을 계속 관리하는 것은 완료 증거와 상호운용성이 전혀 다르다.
|
||||
|
||||
이 ADR은 목표 계약을 정의한다. 이 문서가 존재한다는 사실은 runtime, endpoint,
|
||||
worker 또는 제품 UX가 구현·조합되었다는 뜻이 아니다.
|
||||
|
||||
## 2. 표준 capability 상태
|
||||
|
||||
설계, source 존재, 제품 조합과 플랫폼 한계를 하나의 `enabled` boolean으로 합치지
|
||||
않는다. primary current status는 다음 다섯 값 중 정확히 하나다. 이 taxonomy는
|
||||
선형 maturity model이 아니며 상태 이름만으로 rollout 또는 production readiness를
|
||||
추론하지 않는다.
|
||||
|
||||
| primary status | 의미 |
|
||||
| --- | --- |
|
||||
| `NOT_SELECTED` | 제품 요구, owner, policy 또는 구현 범위가 아직 선택되지 않음 |
|
||||
| `DESIGNED_NOT_IMPLEMENTED` | versioned contract와 불변조건은 승인됐지만 reference source가 없음 |
|
||||
| `AVAILABLE_NOT_COMPOSED` | 검증 가능한 reference source가 있지만 제품 bootstrap/endpoint에는 연결되지 않음 |
|
||||
| `COMPOSED` | 특정 제품 facade, config와 dependency에 실제로 조합됨 |
|
||||
| `PLATFORM_LIMITED` | 요구 semantics를 target browser/platform 전체에서 보장할 수 없음 |
|
||||
|
||||
production readiness와 traffic admission은 primary status와 독립된 축이다.
|
||||
운영 상태는 completion ledger가 정의한 네 canonical 축만 사용한다.
|
||||
|
||||
```text
|
||||
Selection =
|
||||
NOT_SELECTED | SELECTED | REMOVING
|
||||
TrafficAdmission =
|
||||
DISABLED | SHADOW | CANARY | ENABLED
|
||||
RuntimeHealth =
|
||||
UNKNOWN | AVAILABLE | DEGRADED | UNAVAILABLE | INCOMPATIBLE
|
||||
PromotionEvidence =
|
||||
MISSING | PARTIAL | COMPLETE | EXPIRED
|
||||
```
|
||||
|
||||
아래 `source evidence`는 ADR과 reference source의 존재를 설명하는 문서 표기일
|
||||
뿐 canonical readiness 축이 아니다. `DESIGN_REVIEWED`는 native browser나
|
||||
provider 증거가 아니고, `REFERENCE_TESTED`도 ledger의 browser component를
|
||||
`PROMOTABLE` 또는 `PromotionEvidence=COMPLETE`로 만들지 않는다.
|
||||
|
||||
현재 capability별 판정:
|
||||
|
||||
| capability | primary status | source evidence | 비고 |
|
||||
| --- | --- | --- | --- |
|
||||
| whole-object foreground streaming | `AVAILABLE_NOT_COMPOSED` | `REFERENCE_TESTED` | 기존 VD-12 범위 |
|
||||
| browser-managed handoff mechanism | `AVAILABLE_NOT_COMPOSED` | `REFERENCE_TESTED` | 실제 capability issuer는 제품 연결 시 필요 |
|
||||
| Range resumable download | `DESIGNED_NOT_IMPLEMENTED` | `DESIGN_REVIEWED` | 이 ADR의 구현 대상 |
|
||||
| app-managed background download | `NOT_SELECTED` | `DESIGN_REVIEWED` | 제품 요구가 선택될 때만 별도 구현 |
|
||||
| cross-browser app-managed background download guarantee | `PLATFORM_LIMITED` | `DESIGN_REVIEWED` | 공통 baseline으로 promotion 불가 |
|
||||
|
||||
기존 foreground stream과 browser-managed handoff의 source/evidence를 Range나
|
||||
app-managed background download 구현 증거로 재사용하지 않는다.
|
||||
|
||||
## 3. 결정 요약
|
||||
|
||||
1. whole-object foreground streaming, Range resumable download,
|
||||
browser-managed handoff와 app-managed background download를 서로 다른
|
||||
capability와 결과 타입으로 유지한다.
|
||||
2. Range protocol literal은 `RANGE_RESUMABLE_DOWNLOAD_V1`로 고정한다. 기존
|
||||
whole-object presigned contract에 암묵적으로 섞지 않는다.
|
||||
3. resume의 authority는 server-owned immutable generation과 strong validator다.
|
||||
local offset, file name, timestamp 또는 partial byte 존재는 authority가 아니다.
|
||||
4. 각 data-plane capability는 exact representation, start/end range, method,
|
||||
response status/header/length와 expiry를 묶고 한 번만 사용한다.
|
||||
5. checkpoint는 비권한성 recovery metadata만 account-partitioned storage에
|
||||
보관한다. URL, signed query/header, raw ETag, bearer token과 file path는
|
||||
저장하지 않는다.
|
||||
6. destination은 seek/truncate 가능한 명시적 port 또는 owned OPFS staging이다.
|
||||
순차 writable에 검증되지 않은 partial bytes를 append하지 않는다.
|
||||
7. checkpoint offset은 destination segment가 durable하게 commit되고 exact length가
|
||||
재확인된 뒤에만 CAS로 전진한다.
|
||||
8. final success는 destination 전체를 처음부터 다시 읽어 whole-object SHA-256을
|
||||
검증하고 final commit을 마친 경우만 `SAVED_VERIFIED`다.
|
||||
9. browser-managed handoff는 탭 종료 뒤 계속될 수 있는 기본 server-file
|
||||
fallback이지만 결과는 계속 `BROWSER_HANDOFF`다.
|
||||
10. app-managed background download는 cross-browser baseline이 아니다. 별도
|
||||
optional protocol, platform probe, worker control plane과 owned staging이 모두
|
||||
승인된 환경에서만 progressive enhancement로 조합한다.
|
||||
11. browser 차이는 user-agent 문자열이 아니라 capability probe와 정책으로
|
||||
결정한다.
|
||||
|
||||
## 4. Topology와 책임
|
||||
|
||||
```text
|
||||
product download use case
|
||||
-> product-owned download facade
|
||||
-> DownloadStrategySelector
|
||||
-> WHOLE_OBJECT_PICKER_STREAM
|
||||
-> RANGE_RESUMABLE_FOREGROUND
|
||||
-> BROWSER_MANAGED_HANDOFF
|
||||
-> BOUNDED_OBJECT_URL
|
||||
-> APP_MANAGED_BACKGROUND_DOWNLOAD (optional)
|
||||
|
||||
RANGE_RESUMABLE_FOREGROUND
|
||||
-> BFF control plane
|
||||
authorization
|
||||
immutable representation lookup
|
||||
range capability issuance/reissue
|
||||
-> browser RangeDownloadRuntime
|
||||
checkpoint + mutation lock
|
||||
exact HTTP state machine
|
||||
seekable destination or OPFS staging
|
||||
whole-object verification
|
||||
-> object store/BFF byte plane
|
||||
|
||||
APP_MANAGED_BACKGROUND_DOWNLOAD
|
||||
-> window-owned admission and user intent
|
||||
-> worker-specific control plane
|
||||
-> owned OPFS staging
|
||||
-> later foreground export
|
||||
```
|
||||
|
||||
브라우저는 bucket, object key, provider generation locator, signing key 또는 cloud
|
||||
credential을 소유하지 않는다. BFF가 logical resource를 exact immutable
|
||||
representation에 binding한다. direct object-store Range가 해당 binding과
|
||||
capability의 `preconditionMode`가 선택한 exact `If-Range` 또는
|
||||
immutable-generation precondition을 실제로 강제하지 못하면 BFF proxy/relay를
|
||||
사용한다.
|
||||
|
||||
## 5. Versioned Range capability
|
||||
|
||||
### 5.1 Application-visible handle
|
||||
|
||||
application에는 raw URL이나 validator를 노출하지 않는다.
|
||||
|
||||
```text
|
||||
RangeDownloadCapability
|
||||
protocol = RANGE_RESUMABLE_DOWNLOAD_V1
|
||||
opaque identity
|
||||
safe receipt
|
||||
resourceId
|
||||
representationBindingSha256
|
||||
totalByteLength
|
||||
mediaType
|
||||
wholeObjectSha256
|
||||
requestedStart
|
||||
requestedEndExclusive
|
||||
preconditionMode = STRONG_IF_RANGE | IMMUTABLE_GENERATION_PRECONDITION
|
||||
allowWholeObjectFallback
|
||||
expiresAtEpochMs
|
||||
```
|
||||
|
||||
adapter-owned identity vault에는 다음 data-plane binding을 함께 둔다.
|
||||
|
||||
```text
|
||||
exact HTTPS URL/query
|
||||
exact GET method
|
||||
exact origin/path
|
||||
exact Range header
|
||||
exact precondition header/value selected by preconditionMode
|
||||
required response headers
|
||||
allowed statuses = policy-derived exact subset of 200 | 206 | 412 | 416
|
||||
expected representation binding
|
||||
maximum response bytes
|
||||
single-use receipt
|
||||
```
|
||||
|
||||
`representationBindingSha256`는 protocol/version, logical resource, immutable
|
||||
generation, precondition mode별 normalized strong validator 또는 generation
|
||||
binding, exact total length, media type와 expected whole-object digest의 canonical
|
||||
binding이다. 이것은 authorization proof가 아니다. BFF는 client 값을 echo하지
|
||||
않고 registry snapshot에서 직접 재계산한다.
|
||||
|
||||
### 5.2 Strong validator
|
||||
|
||||
resume에는 다음 중 하나가 필요하다.
|
||||
|
||||
- server registry가 소유하는 immutable object generation과 그 generation에 pin된
|
||||
proxy/direct request
|
||||
- RFC semantics를 만족하는 strong ETag와 exact `If-Range`
|
||||
|
||||
weak ETag(`W/`), `Last-Modified`만 있는 representation, multipart ETag를 whole
|
||||
digest로 해석한 값과 CDN이 임의로 다시 쓴 validator는 resume authority로
|
||||
사용하지 않는다. provider가 strong validator를 제공하지 못하면 BFF가 immutable
|
||||
generation을 pin하거나 Range resume를 `UNSUPPORTED`로 닫는다.
|
||||
|
||||
raw ETag와 provider generation locator는 application, checkpoint, diagnostics와
|
||||
telemetry에 노출하지 않는다. reload 뒤에는 BFF가 새 capability를 발급하고,
|
||||
runtime은 새 capability의 `representationBindingSha256`가 checkpoint와 같은지
|
||||
확인한 뒤 vault 안의 exact precondition만 사용한다.
|
||||
|
||||
`STRONG_IF_RANGE` mode는 exact `If-Range`를 보내고 `206`, Range-ignore 또는
|
||||
validator mismatch의 full `200`과 해당 `416`만 계약한다.
|
||||
`IMMUTABLE_GENERATION_PRECONDITION` mode는 BFF/provider가 정한 exact `If-Match`
|
||||
또는 generation precondition을 보내며 `412`를 계약할 수 있다.
|
||||
`allowWholeObjectFallback`과 `allowedStatuses`는 mode, requested start와 provider
|
||||
topology에서 capability 발급 시 닫히며 executor가 임의로 넓히지 않는다.
|
||||
|
||||
### 5.3 Capability 재발급
|
||||
|
||||
capability expiry, data-plane `401/403/410` 또는 최소 잔여 lifetime 부족은 같은
|
||||
URL의 무조건 retry가 아니다.
|
||||
|
||||
1. 현재 response reader를 cancel하고 capability를 consume한다.
|
||||
2. control plane에 `downloadKey`, resource와 expected representation binding,
|
||||
exact next range를 전달한다.
|
||||
3. BFF가 authorization와 current generation을 다시 읽는다.
|
||||
4. binding이 같을 때만 새 capability로 같은 range를 재시도한다.
|
||||
5. binding이 바뀌었으면 partial destination을 append하지 않고
|
||||
`REPRESENTATION_CHANGED/RESTART`로 닫는다.
|
||||
|
||||
재발급 횟수, 전체 operation deadline과 retry backoff는 composition hard ceiling
|
||||
안에 둔다. capability를 durable queue나 worker message에 저장하지 않는다.
|
||||
|
||||
## 6. Durable checkpoint
|
||||
|
||||
### 6.1 Schema
|
||||
|
||||
```text
|
||||
RangeDownloadCheckpointV1
|
||||
schemaVersion = 1
|
||||
protocol = RANGE_RESUMABLE_DOWNLOAD_V1
|
||||
revision
|
||||
state = ACTIVE | PAUSED | FINALIZING | CLEANUP_PENDING
|
||||
downloadKey
|
||||
resourceBindingSha256
|
||||
representationBindingSha256
|
||||
totalByteLength
|
||||
nextOffset
|
||||
committedSegmentCount
|
||||
destination
|
||||
kind = OPFS_STAGING | SEEKABLE_FILE
|
||||
opaqueDestinationBinding
|
||||
createdAtEpochMs
|
||||
updatedAtEpochMs
|
||||
retentionExpiresAtEpochMs
|
||||
```
|
||||
|
||||
`downloadKey`, destination binding과 physical database/OPFS namespace는
|
||||
composition-issued opaque token이다. 사용자 file name, resource ID, account ID,
|
||||
tenant ID 또는 local path를 넣지 않는다.
|
||||
|
||||
checkpoint에 금지하는 값:
|
||||
|
||||
- presigned URL, query와 signed request/response header
|
||||
- bearer/session/auth/CSRF token
|
||||
- raw ETag, provider object key/generation locator
|
||||
- file name, user path와 native exception
|
||||
- incremental hash 내부 state
|
||||
- raw backend response나 retry body
|
||||
|
||||
허용된 digest binding과 offset은 비권한성 recovery metadata다. account partition,
|
||||
retention, count/byte budget과 logout deletion을 적용하며 log/analytics/ticket에는
|
||||
내보내지 않는다.
|
||||
|
||||
### 6.2 CAS와 durable offset
|
||||
|
||||
한 `downloadKey`는 cross-context exclusive mutation lock으로 직렬화한다. lock은
|
||||
correctness의 유일한 authority가 아니며 checkpoint revision CAS와 exact
|
||||
destination binding이 최종 local authority다.
|
||||
|
||||
`nextOffset`은 다음 순서가 모두 성공한 뒤에만 전진한다.
|
||||
|
||||
1. exact `206` range를 bounded stream으로 읽는다.
|
||||
2. expected start 위치에만 쓴다.
|
||||
3. writer close/segment commit을 완료한다.
|
||||
4. destination의 committed length가 expected end 이상인지 확인한다.
|
||||
5. unexpected tail이 있으면 authorized `truncate(expectedEnd)`를 완료한다.
|
||||
6. checkpoint를 `revision + 1`, `nextOffset = expectedEnd`로 CAS한다.
|
||||
|
||||
response가 성공했지만 destination commit 전에 crash하면 checkpoint는 이전
|
||||
offset에 머문다. 재시작은 destination을 checkpoint offset으로 truncate하고 같은
|
||||
range를 다시 요청한다. destination commit 뒤 checkpoint CAS가 유실된 경우도
|
||||
동일하게 checkpoint offset까지 truncate한 뒤 재전송한다. 따라서 중복 byte를
|
||||
append하지 않는다.
|
||||
|
||||
### 6.3 Inventory와 retention
|
||||
|
||||
checkpoint store는 단일 key read 외에 bounded admin operation을 제공해야 한다.
|
||||
|
||||
- account partition 안의 safe summary를 cursor page로 list
|
||||
- expired/terminal checkpoint를 bounded batch로 classify
|
||||
- destination binding과 함께 exact owned staging을 cleanup
|
||||
- active lock/lease가 있는 항목은 건너뜀
|
||||
- count, logical bytes, maximum age와 cleanup retry budget 강제
|
||||
- cleanup receipt를 durable하게 남기고 response 유실을 reconcile
|
||||
|
||||
inventory에는 resource ID, file name, digest, raw validator와 path를 반환하지
|
||||
않는다. 제품 resume UI가 필요한 경우 제품 database/query가 별도 safe display
|
||||
metadata를 소유하고 opaque `downloadKey`로만 연결한다.
|
||||
|
||||
## 7. Destination 계약
|
||||
|
||||
### 7.1 공통 port
|
||||
|
||||
```text
|
||||
ResumableDownloadDestinationPort
|
||||
inspect(binding) -> committedLength, readable, writable, permissionState
|
||||
openWriter(binding, keepExistingData=true)
|
||||
seek(offset)
|
||||
write(chunk)
|
||||
truncate(length)
|
||||
commitSegment()
|
||||
openReader(start=0)
|
||||
finalize()
|
||||
abortAttempt()
|
||||
cleanup(authority)
|
||||
```
|
||||
|
||||
native handle, OPFS handle와 path는 adapter 밖으로 노출하지 않는다. 모든 method는
|
||||
bounded deadline, AbortSignal과 closed failure를 사용한다.
|
||||
|
||||
### 7.2 Seekable external file
|
||||
|
||||
직접 외부 파일에 resume하려면 browser가 기존 data 보존, seek, truncate,
|
||||
재읽기와 permission 재확인을 실제로 지원해야 한다.
|
||||
|
||||
- picker와 permission request는 Window의 명시적 user activation에서만 실행한다.
|
||||
- structured-cloned handle을 보존하는 경우 별도 privacy/retention 승인이 필요하다.
|
||||
- reopen 뒤 `queryPermission`/`requestPermission`을 거치며 denied면
|
||||
`PERMISSION_DENIED/RESELECT`다.
|
||||
- writer가 temporary-file commit semantics를 쓰면 segment마다 close한 뒤
|
||||
committed file size를 다시 확인한다.
|
||||
- checkpoint보다 큰 tail은 검증하지 않고 사용하지 않으며 exact checkpoint
|
||||
offset으로 truncate한다.
|
||||
- checkpoint보다 파일이 작거나 다른 handle이면 `CONFLICT/RESTART`다.
|
||||
|
||||
브라우저가 이 계약을 만족하지 못하면 external-file resume를 흉내 내지 않고 OPFS
|
||||
staging 또는 browser-managed handoff로 전환한다.
|
||||
|
||||
### 7.3 OPFS staging
|
||||
|
||||
cross-browser app-controlled resume의 우선 destination은 policy-owned OPFS
|
||||
staging이다.
|
||||
|
||||
- physical path는 기존 OPFS authority/namespace/partition registry가 발급한다.
|
||||
- checkpoint와 OPFS object는 immutable binding과 generation journal로 연결한다.
|
||||
- quota estimate는 admission hint일 뿐이며 write 중 quota failure도 처리한다.
|
||||
- download 완료 뒤 staging 전체를 다시 읽어 SHA-256을 검증한다.
|
||||
- foreground user activation에서 새 외부 destination을 열고 staging을 stream
|
||||
export한다.
|
||||
- 외부 export close가 성공하기 전 staging을 삭제하지 않는다.
|
||||
- export 결과가 유실되면 staging을 유지하고 user에게 retry 가능한 상태를
|
||||
반환한다.
|
||||
|
||||
OPFS 저장 성공은 사용자가 접근 가능한 파일 저장 완료가 아니다. 결과를
|
||||
`STAGED_VERIFIED`와 `SAVED_VERIFIED`로 구분한다. OPFS는 큰 파일에서 storage와
|
||||
I/O를 한 번 더 요구하므로 quota/retention owner 없는 기본 fallback이 아니다.
|
||||
|
||||
## 8. HTTP 상태 머신
|
||||
|
||||
### 8.1 요청 전
|
||||
|
||||
1. checkpoint와 destination binding을 exact하게 읽는다.
|
||||
2. destination length를 검사하고 checkpoint보다 큰 tail을 truncate한다.
|
||||
3. checkpoint보다 작으면 partial을 신뢰하지 않고 restart/cleanup으로 닫는다.
|
||||
4. 새 capability의 representation binding과 exact range를 검증한다.
|
||||
5. `Range: bytes=S-E`와 capability의 `preconditionMode`가 정한 exact
|
||||
`If-Range` 또는 immutable-generation precondition을 vault binding 그대로
|
||||
보낸다.
|
||||
6. `credentials: omit`, `redirect: error`, `no-referrer`, `no-store`,
|
||||
identity content encoding을 강제한다.
|
||||
|
||||
한 request의 range 크기와 exact `S/E`는 capability 발급 **전에** composition
|
||||
maximum 안에서 계산한다. executor는 capability의 `requestedStart`,
|
||||
`requestedEndExclusive`와 exact Range header가 일치하는지 검증하고 그대로
|
||||
전송하며 다시 줄이거나 늘리지 않는다. ceiling을 넘는 capability는 사용 전에
|
||||
거절한다. 기본 protocol은 sequential range만 허용한다. parallel range와 sparse
|
||||
destination은 별도 protocol/version 없이는 사용하지 않는다.
|
||||
zero-byte representation은 유효하지 않은 byte range를 만들지 않는다. exact
|
||||
length가 0이고 empty-object SHA-256 binding이 일치하는 whole-object `200` 경로로
|
||||
body/length를 확인한 뒤 바로 final verification으로 이동한다.
|
||||
|
||||
response body를 읽거나 destination writer를 열기 전에 `200`, `206`, `412`,
|
||||
`416` 중 수신한 status가 capability vault의 exact `allowedStatuses` member인지
|
||||
검사한다. 해당 네 값 중 허용되지 않은 status는 body를 cancel하고 capability를
|
||||
consume하며 destination과 checkpoint를 변경하지 않은 채
|
||||
`CONTRACT_MISMATCH`로 fail-closed한다. 아래 네 분기는 이 공통 admission gate를
|
||||
통과한 경우에만 실행한다. 그 밖의 status는 §8.6의 별도 failure/reissue 규칙으로
|
||||
처리한다.
|
||||
|
||||
### 8.2 `206 Partial Content`
|
||||
|
||||
성공 조건:
|
||||
|
||||
- `206`이 capability의 `allowedStatuses` member
|
||||
- final response URL이 capability URL과 exact match
|
||||
- `Content-Range: bytes S-E/T`가 하나만 존재하고 parse가 엄격함
|
||||
- `S`가 requested start, `E + 1`이 requested end exclusive
|
||||
- `T`가 checkpoint total과 같음
|
||||
- `Content-Length = E - S + 1`
|
||||
- strong validator/immutable generation binding 일치
|
||||
- media type과 identity encoding 일치
|
||||
- body 실제 bytes가 exact content length
|
||||
|
||||
하나라도 다르면 reader와 current destination attempt를 abort하고 checkpoint를
|
||||
전진시키지 않는다. 정상인 경우에만 앞 절의 durable offset 순서로 commit한다.
|
||||
|
||||
### 8.3 `200 OK`
|
||||
|
||||
`200`은 capability의 `allowedStatuses` member인 경우에만 이 분기로 들어온다.
|
||||
body를 destination에 쓰기 전에 final response URL, required response header,
|
||||
media type, identity encoding과 mode별 strong validator 또는 immutable-generation
|
||||
evidence가 capability의 exact representation binding과 일치하는지 검증한다.
|
||||
|
||||
다음 순서로 배타적으로 처리한다.
|
||||
|
||||
1. validator/generation evidence가 없거나 binding이 다르면 body를 cancel하고
|
||||
capability를 consume한다. 기존 checkpoint와 partial은 append하지 않고
|
||||
quarantine/retention policy로 전환한 뒤 control plane에서 current
|
||||
representation을 다시 확인한다. 결과는
|
||||
`REPRESENTATION_CHANGED/RESTART`이며 byte 0의 새 operation만 허용한다.
|
||||
2. binding은 같지만 requested start가 `0`이고
|
||||
`allowWholeObjectFallback=true`이면 fresh whole-object destination에서 기존
|
||||
whole-object stream 계약으로 처리한다. exact total length와 final
|
||||
whole-object digest를 검증하기 전에는 success나 final commit을 반환하지 않는다.
|
||||
3. binding은 같고 requested start가 `0`이지만
|
||||
`allowWholeObjectFallback=false`이면 body를 한 byte도 쓰지 않고 cancel한다.
|
||||
결과는 `WHOLE_OBJECT_FALLBACK_NOT_ALLOWED`이며 policy가 허용한 새 Range
|
||||
capability, browser handoff 또는 explicit unsupported만 선택한다.
|
||||
4. binding은 같고 requested start가 `0`보다 크면 server가 Range를 무시한
|
||||
것이다. body를 한 byte도 쓰지 않고 cancel하며 기존 partial을 같은 writer에서
|
||||
덮어쓰지 않는다. control plane reconcile 뒤 같은 representation의 byte 0
|
||||
restart operation, browser handoff 또는 explicit unsupported만 선택한다.
|
||||
|
||||
### 8.4 `412 Precondition Failed`
|
||||
|
||||
`412`가 capability의 `allowedStatuses` member이고
|
||||
`preconditionMode=IMMUTABLE_GENERATION_PRECONDITION`인 경우에만 이 분기로 들어온다.
|
||||
representation precondition 실패다. body를 cancel하고 checkpoint를 유지한 채
|
||||
control plane에서 current generation을 확인한다. 같은 binding을 다시 발급하지
|
||||
못하면 partial은 cleanup policy에 따라 폐기하고 byte 0부터 새 operation을
|
||||
시작한다.
|
||||
|
||||
RFC `If-Range` validator mismatch 자체의 정상 응답은 `200`이다. `412`는 BFF나
|
||||
provider가 immutable generation을 pin하기 위해 별도 `If-Match` 계열 precondition을
|
||||
함께 강제하는 topology에서만 이 상태 머신에 들어온다. topology가 `412`를 계약하지
|
||||
않았다면 unknown status로 fail-closed한다.
|
||||
|
||||
### 8.5 `416 Range Not Satisfiable`
|
||||
|
||||
`416`이 capability의 `allowedStatuses` member인 경우에만 이 분기로 들어온다.
|
||||
response body는 download data로 소비하지 않고 cancel한다.
|
||||
`Content-Range: bytes */T`를 strict하게 검사하며 final response URL, required
|
||||
headers와 mode별 validator/generation binding도 확인한다. provider의 `416`이
|
||||
binding evidence를 반환할 수 없는 topology라면 BFF control plane reconcile이
|
||||
exact immutable generation을 다시 증명하기 전에는 EOF나 missing-range 분기로
|
||||
진행하지 않는다.
|
||||
|
||||
다음 순서를 사용하며 한 분기를 처리한 뒤 아래 분기로 fall through하지 않는다.
|
||||
|
||||
1. malformed/missing `T`, final URL/header mismatch 또는 증명되지 않은
|
||||
representation binding은 `CONTRACT_MISMATCH`로 fail-closed한다.
|
||||
2. `T != expected total`이면 representation changed다. local bytes를 `T`에 맞춰
|
||||
자동 truncate하거나 append하지 않고 capability를 consume한 뒤 partial을
|
||||
quarantine/restart한다.
|
||||
3. `nextOffset > T`이면 checkpoint 자체가 corrupt/stale이다. 잘못된 offset으로
|
||||
truncate하지 않고 checkpoint와 partial을 quarantine한 뒤 restart/recovery로
|
||||
닫는다.
|
||||
4. `nextOffset <= T`이지만 `local committed length != nextOffset`이면 먼저 local
|
||||
state를 reconcile한다.
|
||||
- local length가 더 크면 exact `nextOffset`까지만 uncommitted tail을
|
||||
authorized truncate하고 durable length를 다시 확인한다.
|
||||
- local length가 더 작으면 journal이 증명하는 마지막 confirmed segment로
|
||||
destination과 checkpoint를 함께 CAS rollback할 수 있을 때만 복구한다.
|
||||
그렇지 않으면 quarantine/restart한다.
|
||||
이 분기는 reconcile 결과를 새 state-machine invocation에서 다시 평가하며 바로
|
||||
finalization이나 missing-range request로 진행하지 않는다.
|
||||
5. `local committed length == nextOffset == T`이면 data transfer가 끝난 후보로
|
||||
보고 `FINALIZING` whole-object verification으로 이동한다.
|
||||
6. `local committed length == nextOffset < T`이면 local missing range가 남아 있다.
|
||||
새 capability로 exact `nextOffset` range를 재발급한다. 같은 total에 대해
|
||||
satisfiable range가 다시 `416`이면 bounded retry하지 않고
|
||||
`CONTRACT_MISMATCH`로 fail-closed한다.
|
||||
|
||||
`416` 자체를 다운로드 성공으로 간주하지 않는다.
|
||||
|
||||
### 8.6 나머지 상태와 network failure
|
||||
|
||||
| 조건 | 처리 |
|
||||
| --- | --- |
|
||||
| `401/403/410` | bounded capability reissue; binding mismatch면 restart |
|
||||
| `404` | existence-hiding policy에 따라 unavailable/not-found, partial cleanup 예약 |
|
||||
| `409` | server representation/session reconcile |
|
||||
| `429`/모든 `5xx`/network | 동일 exact range만 bounded retry |
|
||||
| redirect/opaque response | policy rejection |
|
||||
| timeout/cancel | reader와 writer attempt abort, checkpoint 유지 |
|
||||
| overrun/truncation | integrity failure, checkpoint 유지 |
|
||||
|
||||
retry는 destination commit 여부를 먼저 판단한다. effect가 ambiguous하면
|
||||
checkpoint와 destination length를 reconcile하기 전 새 offset으로 이동하지 않는다.
|
||||
|
||||
## 9. Whole-object integrity와 final commit
|
||||
|
||||
Range별 transport 검증은 whole-object 무결성 증거가 아니다. 모든 bytes가
|
||||
수신되면 checkpoint를 `FINALIZING`으로 CAS하고 다음을 수행한다.
|
||||
|
||||
1. destination length가 exact total과 같은지 확인한다.
|
||||
2. destination을 byte 0부터 bounded chunk로 다시 읽는다.
|
||||
3. vetted incremental SHA-256으로 whole-object digest를 계산한다.
|
||||
4. capability/representation binding의 expected digest와 constant-time 비교한다.
|
||||
5. mismatch면 사용자 destination을 성공으로 표시하지 않고 staging을 격리하거나
|
||||
authorized cleanup한다.
|
||||
6. OPFS staging이면 foreground external export와 destination close를 완료한다.
|
||||
7. final destination commit truth를 확인한 뒤만 `SAVED_VERIFIED`를 반환한다.
|
||||
8. checkpoint와 staging cleanup을 exact revision/receipt로 완료한다.
|
||||
|
||||
portable하지 않은 incremental hash 내부 state를 checkpoint에 serialize하지 않는다.
|
||||
마지막 full reread 비용을 피하려면 chunk digest/Merkle manifest를 별도 protocol로
|
||||
설계하고 server가 exact proof를 제공해야 한다.
|
||||
|
||||
## 10. Pause, cancel, crash와 account lifecycle
|
||||
|
||||
### 10.1 Pause
|
||||
|
||||
`pause(downloadKey)`는 browser work 중단이며 server resource/capability revoke가
|
||||
아니다.
|
||||
|
||||
- 같은 runtime의 read/write/backoff를 AbortSignal로 중단한다.
|
||||
- same-origin context에는 opaque key만 담은 versioned ephemeral pause event를
|
||||
보낸다.
|
||||
- mutation lock 안에서 checkpoint를 `PAUSED`로 CAS한다.
|
||||
- in-memory URL/header/capability는 즉시 retire한다.
|
||||
- committed segment는 유지하고 ambiguous writer attempt는 checkpoint offset으로
|
||||
reconcile한다.
|
||||
|
||||
### 10.2 Cancel과 discard
|
||||
|
||||
cancel은 transfer 중단만 의미할 수 있고, discard는 local partial 삭제다. 제품
|
||||
facade가 두 의도를 구분해야 한다. discard는 exact partition/destination binding과
|
||||
short-lived maintenance authority를 요구하며 checkpoint와 OPFS staging을 하나의
|
||||
cleanup journal로 처리한다.
|
||||
|
||||
### 10.3 Crash/reload
|
||||
|
||||
reload 후 runtime은:
|
||||
|
||||
1. account partition과 governance binding을 검증한다.
|
||||
2. checkpoint schema/protocol/revision을 검증한다.
|
||||
3. destination을 reopen하고 permission/length를 검사한다.
|
||||
4. server에서 새 capability를 발급받아 representation binding을 대조한다.
|
||||
5. exact checkpoint offset부터 resume한다.
|
||||
|
||||
source가 같은지 사용자에게 묻는 file-name 기반 확인은 사용하지 않는다.
|
||||
|
||||
### 10.4 Logout/account/tenant switch
|
||||
|
||||
- 신규 capability 발급과 resume admission을 먼저 닫는다.
|
||||
- active foreground operation을 abort하고 writer를 정리한다.
|
||||
- vault와 worker channel을 close한다.
|
||||
- account partition의 checkpoint와 owned staging을 maintenance-authorized bounded
|
||||
cleanup으로 제거한다.
|
||||
- blocked deletion을 성공으로 보고하지 않는다.
|
||||
- 이전 account handle/reference를 새 runtime에서 resolve하지 않는다.
|
||||
|
||||
retention/legal-hold 정책이 local partial 보존을 요구하는 특별한 제품이 아니라면
|
||||
logout에서 partial을 제거하는 것이 기본이다.
|
||||
|
||||
## 11. Download strategy selector
|
||||
|
||||
selector는 presentation의 임의 조건문이 아니라 composition-owned immutable
|
||||
policy와 runtime probe를 받는 공통 application service다.
|
||||
|
||||
입력:
|
||||
|
||||
- source가 server resource인지 client-generated artifact인지
|
||||
- exact 또는 maximum byte length
|
||||
- verified integrity 필요 여부
|
||||
- resume/background 요구
|
||||
- system save picker, seek/truncate, OPFS와 worker capability
|
||||
- user activation
|
||||
- storage quota admission
|
||||
- browser-managed capability availability
|
||||
- data classification와 retention policy
|
||||
|
||||
결과:
|
||||
|
||||
| 조건 | 선택 |
|
||||
| --- | --- |
|
||||
| server file, 탭 종료 뒤 계속 필요 | `BROWSER_MANAGED_HANDOFF` |
|
||||
| server file, verified foreground save, picker 지원 | `WHOLE_OBJECT_PICKER_STREAM` |
|
||||
| server file, resume 필수, destination 계약 충족 | `RANGE_RESUMABLE_FOREGROUND` |
|
||||
| 작은 generated artifact | `BOUNDED_OBJECT_URL` |
|
||||
| 큰 generated artifact, picker 지원 | `WHOLE_OBJECT_PICKER_STREAM` |
|
||||
| 큰 generated artifact, picker 미지원 | `SERVER_GENERATION_REQUIRED` 또는 `UNSUPPORTED` |
|
||||
| app background download가 승인·지원되고 OPFS quota 확보 | `APP_MANAGED_BACKGROUND_DOWNLOAD` |
|
||||
|
||||
selector는 fallback으로 byte/memory/security ceiling을 올리지 않는다. integrity가
|
||||
필수인데 browser handoff만 가능하면 “검증된 저장”으로 downgrade하지 않고 제품이
|
||||
handoff 또는 unsupported 중 하나를 명시적으로 선택한다.
|
||||
|
||||
### Safari와 picker 미지원 환경
|
||||
|
||||
user-agent 문자열로 Safari를 판별하지 않는다. 필요한 API와 실제 semantics를
|
||||
capability probe로 확인한다.
|
||||
|
||||
- 대용량 server file: authorized `Content-Disposition` browser handoff
|
||||
- 작은 generated file: bounded Blob/object URL
|
||||
- 대용량 generated file: server-side generation 또는 unsupported
|
||||
- OPFS: app-private staging일 뿐 Finder/Files 저장 완료로 표시하지 않음
|
||||
- system save picker 미지원: unbounded Blob으로 자동 전환하지 않음
|
||||
- seek/truncate/permission semantics 미충족: external Range resume 비활성화
|
||||
|
||||
browser-managed handoff endpoint는 cross-origin `download` attribute에 의존하지
|
||||
않고 server가 safe `Content-Disposition`, media type, byte/generation policy를
|
||||
실제 response에서 강제한다.
|
||||
|
||||
## 12. Background download 전달의 세 의미
|
||||
|
||||
### 12.1 Foreground app-managed
|
||||
|
||||
page가 열린 동안 runtime이 fetch, progress, integrity와 destination을 모두
|
||||
관리한다. 현재 whole-object stream과 목표 Range resume가 이 범주다. page lifecycle
|
||||
종료 뒤 지속을 보장하지 않는다.
|
||||
|
||||
### 12.2 Browser-managed handoff
|
||||
|
||||
navigation/download manager에 authorized endpoint를 넘긴다.
|
||||
|
||||
- page 종료 뒤 계속될 수 있는 가장 넓은 fallback
|
||||
- application은 실제 disk write, 저장 위치와 final digest를 관찰하지 못함
|
||||
- 결과는 `BROWSER_HANDOFF`, `SAVED`나 `VERIFIED`가 아님
|
||||
- pause/resume UI와 retry semantics는 browser가 소유
|
||||
|
||||
### 12.3 App-managed background download
|
||||
|
||||
Service Worker/Background Fetch 등에서 application이 progress/retry/staging을
|
||||
관리하려는 별도 optional capability다.
|
||||
|
||||
필수 조건:
|
||||
|
||||
- target browser/deployment의 explicit support matrix
|
||||
- worker-safe authenticated control plane
|
||||
- worker가 매 range마다 새 short-lived capability를 발급받는 계약
|
||||
- capability/URL/header를 IDB, OPFS, Cache Storage와 message에 저장하지 않음
|
||||
- private bytes는 Cache Storage가 아니라 policy-owned OPFS staging 사용
|
||||
- worker termination을 정상 상태로 보고 checkpoint에서 재개
|
||||
- concurrency, battery/network, quota와 retention ceiling
|
||||
- logout/revocation event와 worker admission fence
|
||||
- client/worker version compatibility와 upgrade drain
|
||||
- notification/foreground export UX
|
||||
|
||||
일반 Service Worker의 수명이나 background execution 시간을 correctness 근거로
|
||||
삼지 않는다. Background Fetch가 없는 환경에서 timer/keepalive로 장기 download를
|
||||
흉내 내지 않는다. user-visible external save picker는 worker에서 호출하지 않고
|
||||
완료된 OPFS staging을 다음 foreground user gesture에서 export한다.
|
||||
|
||||
따라서 app-managed background download가 향후 `AVAILABLE_NOT_COMPOSED` 또는 `COMPOSED`가
|
||||
되더라도 지원 browser의 progressive enhancement일 뿐이다. cross-browser 보장
|
||||
자체의 primary status는 계속 `PLATFORM_LIMITED`다.
|
||||
|
||||
## 13. Security, privacy와 observability
|
||||
|
||||
- URL/query/header, validator와 capability는 bearer 또는 sensitive metadata로
|
||||
취급한다.
|
||||
- `Range`와 `preconditionMode`가 선택한 exact `If-Range` 또는
|
||||
immutable-generation precondition은 adapter vault가 binding 그대로 생성한다.
|
||||
- caller는 offset을 늘리거나 arbitrary range를 요청하지 못한다.
|
||||
- account partition과 resource authorization을 매 capability reissue에서 검사한다.
|
||||
- partial bytes는 원본과 같은 data classification, retention, encryption-at-rest와
|
||||
deletion policy를 적용한다.
|
||||
- OPFS quota pressure가 다른 account partial을 제거할 권한을 주지 않는다.
|
||||
- preview, execution 또는 Cache Storage promotion은 final verification 전 금지한다.
|
||||
- high-cardinality ID, file name, path, URL, raw ETag와 digest를 metric label/log에
|
||||
넣지 않는다.
|
||||
|
||||
허용된 aggregate observation:
|
||||
|
||||
- strategy와 destination kind
|
||||
- response state bucket
|
||||
- expected/committed byte bucket
|
||||
- retry/reissue/resume count bucket
|
||||
- duration, pause, restart, integrity와 cleanup outcome
|
||||
- browser capability support reason code
|
||||
|
||||
## 14. Composition과 operational admission
|
||||
|
||||
`createBrowserTransferRuntime`에 해당하는 미래 composition owner만 다음을 조합한다.
|
||||
|
||||
- versioned wire codecs와 fixed BFF endpoint
|
||||
- capability vault/provider/executor
|
||||
- Range checkpoint store, destination registry와 mutation lock
|
||||
- selector policy와 browser capability probe
|
||||
- presigned, upload, image와 Range lifecycle
|
||||
- account/logout cleanup authority
|
||||
- safe observer
|
||||
- traffic admission과 kill switch
|
||||
|
||||
독립적인 canonical readiness 상태:
|
||||
|
||||
```text
|
||||
Selection = NOT_SELECTED | SELECTED | REMOVING
|
||||
TrafficAdmission = DISABLED | SHADOW | CANARY | ENABLED
|
||||
RuntimeHealth = UNKNOWN | AVAILABLE | DEGRADED | UNAVAILABLE | INCOMPATIBLE
|
||||
PromotionEvidence = MISSING | PARTIAL | COMPLETE | EXPIRED
|
||||
```
|
||||
|
||||
primary status가 `COMPOSED`여도 `TrafficAdmission` 기본값은 `DISABLED`다.
|
||||
필수 config, strong validator/provider conformance, destination semantics, cleanup
|
||||
owner 또는 valid evidence가 없으면 `TrafficAdmission=DISABLED`,
|
||||
`RuntimeHealth=UNKNOWN | UNAVAILABLE`,
|
||||
`PromotionEvidence=MISSING | PARTIAL | EXPIRED`로 readiness를 fail-closed한다.
|
||||
이미 승인된 product selection 자체를 provider evidence 부족만으로 되돌리지
|
||||
않는다.
|
||||
|
||||
Kill switch:
|
||||
|
||||
- 신규 Range capability issuance off
|
||||
- Range resume off → whole-object restart 또는 browser handoff
|
||||
- direct object-store Range off → BFF proxy
|
||||
- external seek destination off → OPFS staging 또는 handoff
|
||||
- app-managed background download off → foreground/browser handoff
|
||||
- final export off → verified staging 유지
|
||||
|
||||
kill switch는 partial을 자동 삭제하거나 handoff를 saved/verified로 바꾸지 않는다.
|
||||
|
||||
## 15. Test와 conformance matrix
|
||||
|
||||
### 15.1 Deterministic runtime
|
||||
|
||||
- checkpoint CAS conflict와 corrupt/unknown field
|
||||
- exact segment commit 전/후 crash
|
||||
- destination larger/smaller/different binding
|
||||
- pause/resume/cancel/discard races
|
||||
- capability expiry/reissue와 representation change
|
||||
- `200/206/412/416` 모든 분기
|
||||
- malformed/multiple/overflow `Content-Range`
|
||||
- weak/missing/mismatched validator
|
||||
- overrun, truncation, stalled body와 abort
|
||||
- final whole-object digest mismatch
|
||||
- cleanup response loss와 replay
|
||||
- count/byte/age retention sweep
|
||||
|
||||
### 15.2 Browser matrix
|
||||
|
||||
- system picker 지원/미지원
|
||||
- seek/truncate/keep-existing-data semantics
|
||||
- OPFS quota, eviction, reload와 worker termination
|
||||
- cross-tab lock/pause delivery
|
||||
- user activation과 permission denied/revoked
|
||||
- large server handoff
|
||||
- foreground export close/abort
|
||||
- Chromium, Firefox와 WebKit 동일 필수 case set
|
||||
|
||||
지원하지 않는 API는 skip이 아니라 selector의 expected fallback/`UNSUPPORTED` 결과로
|
||||
검증한다.
|
||||
|
||||
### 15.3 BFF/object provider contract
|
||||
|
||||
- immutable generation pin
|
||||
- capability `preconditionMode`에 따른 strong `If-Range` 또는 immutable
|
||||
generation precondition
|
||||
- beginning/middle/end/empty/invalid range
|
||||
- exact `206 Content-Range`와 length
|
||||
- deliberate Range ignore `200`
|
||||
- mode가 계약한 경우의 precondition `412`, EOF/invalid `416`
|
||||
- mid-transfer capability expiry/revocation
|
||||
- redirect/CORS/exposed-header/identity-encoding
|
||||
- object replacement race
|
||||
- direct provider와 proxy 결과 동등성
|
||||
- URL/header/log redaction
|
||||
|
||||
fake와 route interception은 actual provider conformance를 대체하지 않는다.
|
||||
|
||||
### 15.4 Background-download-specific fault
|
||||
|
||||
- worker가 range commit 전/후 종료
|
||||
- worker/client version 교체
|
||||
- logout과 capability revocation
|
||||
- offline/online 반복, quota exhaustion과 battery/network policy
|
||||
- notification 유실과 foreground export replay
|
||||
- unsupported browser가 foreground/handoff로 정확히 fallback
|
||||
|
||||
## 16. Rollout과 promotion
|
||||
|
||||
Primary status 변경과 readiness/traffic promotion은 별도로 승인한다.
|
||||
|
||||
1. Range의 `DESIGNED_NOT_IMPLEMENTED`와 ADR-local
|
||||
`sourceEvidence=DESIGN_REVIEWED`를 확인한다.
|
||||
2. provider-neutral ports/runtime, deterministic fake, negative fixture와 browser
|
||||
test를 완성한 경우에만 Range primary status를
|
||||
`AVAILABLE_NOT_COMPOSED`, source evidence를 `REFERENCE_TESTED`로 변경한다.
|
||||
deterministic/reference test만으로 canonical `PromotionEvidence`를
|
||||
`COMPLETE`로 바꾸지 않는다.
|
||||
3. 제품 요구, owner, data class와 fallback이 선택되지 않은 app-managed
|
||||
background download는 계속 `NOT_SELECTED`로 둔다. cross-browser 보장은
|
||||
`PLATFORM_LIMITED`다.
|
||||
4. fixed staging BFF/provider, actual config, account lifecycle와 runbook을 설치한
|
||||
capability만 `COMPOSED`로 기록한다. 이때도
|
||||
`TrafficAdmission=DISABLED`, `RuntimeHealth=UNKNOWN`,
|
||||
`PromotionEvidence=PARTIAL`이다.
|
||||
5. operator probe와 shadow에서 contract evidence를 수집한다.
|
||||
6. internal cohort에서 BFF proxy Range를 먼저 canary한다.
|
||||
7. direct provider Range와 external seek destination은 각각 별도 canary한다.
|
||||
8. app-managed background download를 실제로 선택했다면 지원 browser cohort에서만 별도
|
||||
opt-in canary한다.
|
||||
9. contract/provider/browser/operations component gate, SLO, cleanup drill,
|
||||
rollback과 evidence freshness가 모두 충족된 승인 범위만
|
||||
`PromotionEvidence=COMPLETE`, `RuntimeHealth=AVAILABLE`,
|
||||
`TrafficAdmission=ENABLED`로 promotion한다.
|
||||
|
||||
provider, endpoint, validator semantics, browser major behavior, destination adapter,
|
||||
wire protocol 또는 security policy가 바뀌면 relevant evidence를 만료시키고
|
||||
재승인한다.
|
||||
|
||||
## 17. Rollback과 제거
|
||||
|
||||
운영 rollback 순서:
|
||||
|
||||
1. 신규 Range/background-download admission과 capability 발급을 중지한다.
|
||||
2. active writer/worker를 abort하고 checkpoint offset으로 reconcile한다.
|
||||
3. app background download를 foreground/browser handoff로 낮춘다.
|
||||
4. direct Range를 BFF proxy 또는 whole-object restart로 낮춘다.
|
||||
5. verified OPFS staging은 retention window 안에서 foreground export 가능 상태로
|
||||
유지한다.
|
||||
6. ambiguous partial은 성공으로 표시하지 않고 cleanup queue로 넘긴다.
|
||||
7. provider/signing credential 노출이 원인이면 backend revoke와 key rotation을
|
||||
수행한다.
|
||||
|
||||
완전 제거:
|
||||
|
||||
1. pending checkpoint/staging inventory를 bounded하게 drain, export 또는 discard한다.
|
||||
2. worker, channel, lock과 runtime을 close한다.
|
||||
3. account-partition checkpoint/OPFS namespace를 maintenance-authorized cleanup한다.
|
||||
4. endpoint, worker registration, config, policy와 feature facade를 제거한다.
|
||||
5. production bundle/module inventory와 removal test로 source 부재를 증명한다.
|
||||
|
||||
rollback은 unbounded Blob fallback, validator 완화, digest 생략 또는 partial 자동
|
||||
append를 허용하지 않는다.
|
||||
|
||||
## 18. 완료 기준
|
||||
|
||||
Range resumable download는 다음이 모두 참일 때만 구현 완료다.
|
||||
|
||||
- `RANGE_RESUMABLE_DOWNLOAD_V1` port와 strict wire codec이 있음
|
||||
- capability mode별 exact allowed-status subset과 `200/206/412/416` 처리
|
||||
상태 머신이 실행 가능하게 검증됨
|
||||
- strong validator/immutable generation이 실제 provider에서 강제됨
|
||||
- checkpoint CAS, inventory, retention과 account cleanup이 구현됨
|
||||
- seek/truncate 또는 OPFS staging destination이 crash fault를 통과함
|
||||
- capability reissue가 representation mismatch를 fail-closed함
|
||||
- final whole-object SHA-256 뒤에만 verified success를 반환함
|
||||
- selector가 picker/seek 미지원과 대용량 fallback을 안전하게 결정함
|
||||
- actual BFF/provider와 Chromium/Firefox/WebKit evidence가 유효함
|
||||
- SLO, alert, runbook, kill switch, rollback과 cleanup drill이 승인됨
|
||||
|
||||
app-managed background download는 위 항목에 더해 다음이 필요하다.
|
||||
|
||||
- 지원 browser/deployment 범위가 명시됨
|
||||
- worker lifecycle 종료를 checkpoint로 복구함
|
||||
- worker control plane이 durable capability 저장 없이 동작함
|
||||
- logout/revocation/version upgrade fault가 통과함
|
||||
- 미지원 browser fallback이 동일 제품 요구를 안전하게 만족하거나 명시적
|
||||
unsupported UX를 가짐
|
||||
|
||||
이 기준 전에는 기존 foreground streaming 또는 browser handoff의 성공을 Range나
|
||||
background download 구현 완료 증거로 사용하지 않는다.
|
||||
|
||||
## 19. 선택하지 않은 대안
|
||||
|
||||
- `Range` header만 추가하고 기존 sequential writable에 append
|
||||
- weak ETag나 file name/lastModified를 representation identity로 사용
|
||||
- serialized incremental hash state를 검증 없이 checkpoint
|
||||
- `200` response를 기존 partial 뒤에 append
|
||||
- `416`을 곧바로 success로 해석
|
||||
- Service Worker keepalive를 cross-browser background 보장으로 간주
|
||||
- picker 미지원 대용량 파일을 unbounded Blob으로 fallback
|
||||
- OPFS staging을 사용자 파일 저장 완료로 표시
|
||||
- browser-managed handoff를 application-verified save로 표시
|
||||
- user-agent 문자열 기반 Safari 분기
|
||||
|
||||
## 20. 참고
|
||||
|
||||
- [Browser data capability completion ledger](../browser-data-capability-completion-ledger.md)
|
||||
- [VD-16 Browser transfer composition과 Image delivery](./VD-16-browser-transfer-composition-and-image-delivery.md)
|
||||
- [Browser transfer recovery](../../operations/browser-transfer-recovery.md)
|
||||
- [RFC 9110 HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110.html)
|
||||
- [Fetch Standard](https://fetch.spec.whatwg.org/)
|
||||
- [File System Standard](https://fs.spec.whatwg.org/)
|
||||
- [Service Workers](https://w3c.github.io/ServiceWorker/)
|
||||
- [Background Fetch draft](https://wicg.github.io/background-fetch/)
|
||||
- [기존 transfer 설계](../presigned-transfer-and-image-cdn.md)
|
||||
- [browser file/origin storage 설계](../browser-file-and-origin-storage.md)
|
||||
Reference in New Issue
Block a user