42 KiB
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 축만 사용한다.
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. 결정 요약
- whole-object foreground streaming, Range resumable download, browser-managed handoff와 app-managed background download를 서로 다른 capability와 결과 타입으로 유지한다.
- Range protocol literal은
RANGE_RESUMABLE_DOWNLOAD_V1로 고정한다. 기존 whole-object presigned contract에 암묵적으로 섞지 않는다. - resume의 authority는 server-owned immutable generation과 strong validator다. local offset, file name, timestamp 또는 partial byte 존재는 authority가 아니다.
- 각 data-plane capability는 exact representation, start/end range, method, response status/header/length와 expiry를 묶고 한 번만 사용한다.
- checkpoint는 비권한성 recovery metadata만 account-partitioned storage에 보관한다. URL, signed query/header, raw ETag, bearer token과 file path는 저장하지 않는다.
- destination은 seek/truncate 가능한 명시적 port 또는 owned OPFS staging이다. 순차 writable에 검증되지 않은 partial bytes를 append하지 않는다.
- checkpoint offset은 destination segment가 durable하게 commit되고 exact length가 재확인된 뒤에만 CAS로 전진한다.
- final success는 destination 전체를 처음부터 다시 읽어 whole-object SHA-256을
검증하고 final commit을 마친 경우만
SAVED_VERIFIED다. - browser-managed handoff는 탭 종료 뒤 계속될 수 있는 기본 server-file
fallback이지만 결과는 계속
BROWSER_HANDOFF다. - app-managed background download는 cross-browser baseline이 아니다. 별도 optional protocol, platform probe, worker control plane과 owned staging이 모두 승인된 환경에서만 progressive enhancement로 조합한다.
- browser 차이는 user-agent 문자열이 아니라 capability probe와 정책으로 결정한다.
4. Topology와 책임
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를 노출하지 않는다.
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을 함께 둔다.
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가 아니다.
- 현재 response reader를 cancel하고 capability를 consume한다.
- control plane에
downloadKey, resource와 expected representation binding, exact next range를 전달한다. - BFF가 authorization와 current generation을 다시 읽는다.
- binding이 같을 때만 새 capability로 같은 range를 재시도한다.
- 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
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은 다음 순서가 모두 성공한 뒤에만 전진한다.
- exact
206range를 bounded stream으로 읽는다. - expected start 위치에만 쓴다.
- writer close/segment commit을 완료한다.
- destination의 committed length가 expected end 이상인지 확인한다.
- unexpected tail이 있으면 authorized
truncate(expectedEnd)를 완료한다. - 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
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 요청 전
- checkpoint와 destination binding을 exact하게 읽는다.
- destination length를 검사하고 checkpoint보다 큰 tail을 truncate한다.
- checkpoint보다 작으면 partial을 신뢰하지 않고 restart/cleanup으로 닫는다.
- 새 capability의 representation binding과 exact range를 검증한다.
Range: bytes=S-E와 capability의preconditionMode가 정한 exactIf-Range또는 immutable-generation precondition을 vault binding 그대로 보낸다.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의allowedStatusesmember- final response URL이 capability URL과 exact match
Content-Range: bytes S-E/T가 하나만 존재하고 parse가 엄격함S가 requested start,E + 1이 requested end exclusiveT가 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과 일치하는지 검증한다.
다음 순서로 배타적으로 처리한다.
- validator/generation evidence가 없거나 binding이 다르면 body를 cancel하고
capability를 consume한다. 기존 checkpoint와 partial은 append하지 않고
quarantine/retention policy로 전환한 뒤 control plane에서 current
representation을 다시 확인한다. 결과는
REPRESENTATION_CHANGED/RESTART이며 byte 0의 새 operation만 허용한다. - binding은 같지만 requested start가
0이고allowWholeObjectFallback=true이면 fresh whole-object destination에서 기존 whole-object stream 계약으로 처리한다. exact total length와 final whole-object digest를 검증하기 전에는 success나 final commit을 반환하지 않는다. - binding은 같고 requested start가
0이지만allowWholeObjectFallback=false이면 body를 한 byte도 쓰지 않고 cancel한다. 결과는WHOLE_OBJECT_FALLBACK_NOT_ALLOWED이며 policy가 허용한 새 Range capability, browser handoff 또는 explicit unsupported만 선택한다. - 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하지 않는다.
- malformed/missing
T, final URL/header mismatch 또는 증명되지 않은 representation binding은CONTRACT_MISMATCH로 fail-closed한다. T != expected total이면 representation changed다. local bytes를T에 맞춰 자동 truncate하거나 append하지 않고 capability를 consume한 뒤 partial을 quarantine/restart한다.nextOffset > T이면 checkpoint 자체가 corrupt/stale이다. 잘못된 offset으로 truncate하지 않고 checkpoint와 partial을 quarantine한 뒤 restart/recovery로 닫는다.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로 진행하지 않는다.
- local length가 더 크면 exact
local committed length == nextOffset == T이면 data transfer가 끝난 후보로 보고FINALIZINGwhole-object verification으로 이동한다.local committed length == nextOffset < T이면 local missing range가 남아 있다. 새 capability로 exactnextOffsetrange를 재발급한다. 같은 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하고 다음을 수행한다.
- destination length가 exact total과 같은지 확인한다.
- destination을 byte 0부터 bounded chunk로 다시 읽는다.
- vetted incremental SHA-256으로 whole-object digest를 계산한다.
- capability/representation binding의 expected digest와 constant-time 비교한다.
- mismatch면 사용자 destination을 성공으로 표시하지 않고 staging을 격리하거나 authorized cleanup한다.
- OPFS staging이면 foreground external export와 destination close를 완료한다.
- final destination commit truth를 확인한 뒤만
SAVED_VERIFIED를 반환한다. - 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은:
- account partition과 governance binding을 검증한다.
- checkpoint schema/protocol/revision을 검증한다.
- destination을 reopen하고 permission/length를 검사한다.
- server에서 새 capability를 발급받아 representation binding을 대조한다.
- 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-Dispositionbrowser 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가 선택한 exactIf-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 상태:
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에 따른 strongIf-Range또는 immutable generation precondition - beginning/middle/end/empty/invalid range
- exact
206 Content-Range와 length - deliberate Range ignore
200 - mode가 계약한 경우의 precondition
412, EOF/invalid416 - 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은 별도로 승인한다.
- Range의
DESIGNED_NOT_IMPLEMENTED와 ADR-localsourceEvidence=DESIGN_REVIEWED를 확인한다. - provider-neutral ports/runtime, deterministic fake, negative fixture와 browser
test를 완성한 경우에만 Range primary status를
AVAILABLE_NOT_COMPOSED, source evidence를REFERENCE_TESTED로 변경한다. deterministic/reference test만으로 canonicalPromotionEvidence를COMPLETE로 바꾸지 않는다. - 제품 요구, owner, data class와 fallback이 선택되지 않은 app-managed
background download는 계속
NOT_SELECTED로 둔다. cross-browser 보장은PLATFORM_LIMITED다. - fixed staging BFF/provider, actual config, account lifecycle와 runbook을 설치한
capability만
COMPOSED로 기록한다. 이때도TrafficAdmission=DISABLED,RuntimeHealth=UNKNOWN,PromotionEvidence=PARTIAL이다. - operator probe와 shadow에서 contract evidence를 수집한다.
- internal cohort에서 BFF proxy Range를 먼저 canary한다.
- direct provider Range와 external seek destination은 각각 별도 canary한다.
- app-managed background download를 실제로 선택했다면 지원 browser cohort에서만 별도 opt-in canary한다.
- 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 순서:
- 신규 Range/background-download admission과 capability 발급을 중지한다.
- active writer/worker를 abort하고 checkpoint offset으로 reconcile한다.
- app background download를 foreground/browser handoff로 낮춘다.
- direct Range를 BFF proxy 또는 whole-object restart로 낮춘다.
- verified OPFS staging은 retention window 안에서 foreground export 가능 상태로 유지한다.
- ambiguous partial은 성공으로 표시하지 않고 cleanup queue로 넘긴다.
- provider/signing credential 노출이 원인이면 backend revoke와 key rotation을 수행한다.
완전 제거:
- pending checkpoint/staging inventory를 bounded하게 drain, export 또는 discard한다.
- worker, channel, lock과 runtime을 close한다.
- account-partition checkpoint/OPFS namespace를 maintenance-authorized cleanup한다.
- endpoint, worker registration, config, policy와 feature facade를 제거한다.
- production bundle/module inventory와 removal test로 source 부재를 증명한다.
rollback은 unbounded Blob fallback, validator 완화, digest 생략 또는 partial 자동 append를 허용하지 않는다.
18. 완료 기준
Range resumable download는 다음이 모두 참일 때만 구현 완료다.
RANGE_RESUMABLE_DOWNLOAD_V1port와 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. 선택하지 않은 대안
Rangeheader만 추가하고 기존 sequential writable에 append- weak ETag나 file name/lastModified를 representation identity로 사용
- serialized incremental hash state를 검증 없이 checkpoint
200response를 기존 partial 뒤에 append416을 곧바로 success로 해석- Service Worker keepalive를 cross-browser background 보장으로 간주
- picker 미지원 대용량 파일을 unbounded Blob으로 fallback
- OPFS staging을 사용자 파일 저장 완료로 표시
- browser-managed handoff를 application-verified save로 표시
- user-agent 문자열 기반 Safari 분기