chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,320 @@
|
||||
# Browser transfer and Image CDN recovery
|
||||
|
||||
이 runbook은 presigned capability, multipart/resumable upload, streaming download와
|
||||
Image CDN reference runtime을 제품에 조합한 경우에만 적용한다. skeleton의 기본
|
||||
bootstrap은 이 runtime을 실행하지 않는다.
|
||||
|
||||
현재 Range resumable download와 top-level transfer/Image descriptor composition은
|
||||
각각 `DESIGNED_NOT_IMPLEMENTED`다. app-managed background download와 upload는
|
||||
각각 `NOT_SELECTED`, 그 cross-browser guarantee는 `PLATFORM_LIMITED`다. 아래에서
|
||||
이 기능을 다루는 절차는 VD-14/VD-16 구현과 제품 조합이 완료된 뒤에만
|
||||
활성화한다. 기존 whole-object streaming, foreground upload resume나
|
||||
browser-managed handoff를 그 기능의 운영 증거로 사용하지 않는다.
|
||||
|
||||
## 1. 관측 신호
|
||||
|
||||
허용된 aggregate 신호:
|
||||
|
||||
- capability issue/claim의 success, expired, policy-rejected bucket
|
||||
- upload session create/resume/reconcile/complete/abort outcome
|
||||
- part size/count/concurrency와 retry bucket
|
||||
- download expected/actual byte bucket과 truncated/overrun/integrity outcome
|
||||
- Image CDN preset/format/width/candidate bucket과 policy rejection
|
||||
- session age, orphan count, quarantine backlog와 promotion latency
|
||||
|
||||
금지:
|
||||
|
||||
- presigned URL, query, signed request/response header
|
||||
- resource/session/asset/account ID와 object key
|
||||
- file name, local path, raw ETag, checksum, receipt/token의 실제 값
|
||||
|
||||
strict upload checkpoint의 protocol-defined SHA-256 fingerprint/part checksum과
|
||||
bounded opaque non-authorizing part receipt는 server reconcile을 위한 durable
|
||||
예외다. account partition/retention 안에서만 보존하고 관측 신호나 ticket에는
|
||||
절대 투영하지 않는다.
|
||||
- raw backend/body/DOMException message와 stack
|
||||
|
||||
## 2. 초기 분류
|
||||
|
||||
| 증상 | 우선 확인 |
|
||||
| --- | --- |
|
||||
| capability expiry/403 급증 | BFF/object-store clock, signer credential rotation, expiry ceiling |
|
||||
| CORS/preflight 실패 | exact origin, method, signed headers, exposed receipt/checksum headers |
|
||||
| redirect/network failure | proxy/CDN redirect 변경, `redirect:error`, URL origin/path policy |
|
||||
| 특정 part 반복 실패 | exact offset/length/checksum/idempotency, capability expiry |
|
||||
| resume conflict | server status와 checkpoint revision/part binding |
|
||||
| status 404/410 반복 | terminal checkpoint 제거와 새 session 생성 여부 |
|
||||
| complete 뒤 file 불일치 | ordered parts, checksum type, ETag 오해, scan/promotion |
|
||||
| download truncation/overrun | capability length, content encoding, proxy buffering/transform |
|
||||
| destination partial save | picker writable abort/close, integrity-before-close |
|
||||
| image policy rejection 급증 | asset revision, preset registry, dimensions, format/CDN origin |
|
||||
| stale/broken private image | lazy load 시점과 signed URL minimum lifetime |
|
||||
| image timeout/decode 실패 급증 | encoded cap, pre-decode metadata, decoded-byte ceiling, probe deadline |
|
||||
| CDN cache poison 의심 | immutable revision, cache key, `Vary`, source asset registry |
|
||||
|
||||
## 3. Presigned capability
|
||||
|
||||
1. 신규 capability 발급을 중지할 kill switch가 동작하는지 확인한다.
|
||||
2. BFF와 object storage의 clock skew와 signer credential lifetime을 확인한다.
|
||||
3. whole-object control plane이 versioned target을 구현한 제품이면 request,
|
||||
response, vault와 executor의 `PRESIGNED_TRANSFER_V1` exact match 및
|
||||
unknown/missing version rejection을 확인한다. 현재 reference wire에 이
|
||||
target이 아직 없으면 구현되지 않은 protocol을 통과했다고 기록하지 않는다.
|
||||
4. capability가 method, object/part, content constraints와 expiry를 server-side로
|
||||
강제하는지 확인한다.
|
||||
5. upload part 발급 BFF가 `sessionId`로 server session을 조회하고
|
||||
`PRESIGNED_MULTIPART_V1` canonical request/upload binding과 part plan을
|
||||
재계산하는지, `UPLOAD_PART` capability binding에도 exact protocol을 넣는지
|
||||
확인한다. client digest 일치만으로 authorization하지 않는다.
|
||||
6. redirect가 추가되었으면 allowlist를 넓히지 말고 direct endpoint를 복구한다.
|
||||
7. URL/query를 ticket 또는 log에 복사하지 않는다. 노출 가능성이 있으면 signer
|
||||
credential/policy와 해당 capability scope를 폐기한다.
|
||||
8. 만료 capability를 retry하지 않고 control plane에서 새 capability를 발급한다.
|
||||
9. client claim은 server replay authority가 아니므로 중복 사용 여부는 server
|
||||
access/audit의 안전한 aggregate로 판단한다.
|
||||
|
||||
## 4. Multipart/resumable upload
|
||||
|
||||
### 4.1 안전한 일시 중지
|
||||
|
||||
현재 runtime에는 공식 `pause()`와 checkpoint inventory/retention sweep이 없다.
|
||||
caller abort 뒤 checkpoint가 남아 재개될 수 있다는 사실을 `PAUSED` 완료로
|
||||
기록하지 않는다. 다음 절차의 `PAUSED` state와 탭 간 pause는 VD-16 목표
|
||||
runtime이 구현된 제품에만 적용한다.
|
||||
|
||||
1. 신규 session create를 중지한다.
|
||||
2. active part는 사용자가 취소하지 않았다면 현재 bounded attempt만 마무리한다.
|
||||
3. complete를 중지하고 server status reconcile만 허용한다.
|
||||
4. presigned URL은 checkpoint에 쓰지 않고 즉시 메모리에서 폐기한다.
|
||||
5. 현재 runtime은 checkpoint를 `ACTIVE` 또는 `ABORT_PENDING`으로 유지한다.
|
||||
목표 pause runtime은 exact scope/generation을 확인해
|
||||
`PAUSE_REQUESTED -> PAUSED`로 전환한다.
|
||||
6. 목표 pause runtime은 cross-context
|
||||
`RESUMABLE_UPLOAD_PAUSE_V1`, checkpoint `schemaVersion: 2`와
|
||||
`ACTIVE | PAUSED | ABORT_PENDING` closed state를 사용한다. v1 old writer
|
||||
drain, historical migration과 N-1 fail-closed를 확인한다.
|
||||
7. 목표 runtime의 inventory/sweep는 count/cursor/deadline 안에서 abandoned와
|
||||
expired 후보만 반환하고, server reconcile/CAS 없이 checkpoint를 삭제하지
|
||||
않는다.
|
||||
|
||||
### 4.2 Resume
|
||||
|
||||
1. session/checkpoint/control DTO의 protocol이 모두
|
||||
`PRESIGNED_MULTIPART_V1`인지 확인하고, session expiry와 source
|
||||
binding/total bytes/part layout을 검사한다.
|
||||
2. server-authoritative status/list-parts를 읽는다.
|
||||
3. 완료 part의 local range SHA-256을 다시 계산해 exact checksum/receipt와 대조한다.
|
||||
4. checkpoint-only part와 server-only part를 자동 complete하지 않는다.
|
||||
5. 불일치는 session을 격리하고 새 session/명시적 abort를 선택한다.
|
||||
6. missing part만 동일 bytes/checksum/idempotency binding으로 전송한다.
|
||||
7. complete 직전에 1부터 연속적인 ordered receipt set을 다시 확인한다.
|
||||
8. status HTTP 404/410 또는 `NOT_FOUND`/`EXPIRED`이면 stale checkpoint를 CAS
|
||||
제거하고 새 session으로 restart한다. 같은 terminal session을 반복 조회하지
|
||||
않는다.
|
||||
9. network/429/모든 5xx retry가 attempt count, `Retry-After`, backoff와
|
||||
operation timeout ceiling 안인지 확인한다.
|
||||
|
||||
### 4.3 PUT acknowledgement
|
||||
|
||||
1. capability가 expected success status, receipt response header와
|
||||
`expectedResponseByteLength`를 exact하게 묶는지 확인한다.
|
||||
2. 실제 `Content-Length`가 expected bytes와 같고 hard response cap 이하인지
|
||||
확인한다. 204는 expected bytes가 0이어야 하며 header 부재를 0으로 정규화한다.
|
||||
3. response body EOF까지 동일 deadline 안에서 bounded drain된 뒤 receipt가
|
||||
accepted됐는지 확인한다.
|
||||
4. receipt/ETag는 part acknowledgement일 뿐 whole-file digest나 authorization
|
||||
proof로 해석하지 않는다.
|
||||
|
||||
### 4.4 Abort/orphan
|
||||
|
||||
- 사용자 cancel은 browser work cancel이며 server abort가 아니다.
|
||||
- 다른 tab에서 명시적 abort하면 `RESUMABLE_UPLOAD_CANCEL_V1`의 opaque
|
||||
upload-key-only BroadcastChannel 신호가 active read/fetch/backoff를 먼저
|
||||
중단하고 Web Lock이 bounded 시간 안에 반환되는지 확인한다.
|
||||
- BroadcastChannel 미지원/정책 차단 환경에서는 abort caller deadline 안에
|
||||
lock을 얻지 못하면 성공으로 가장하지 않고 `ABORT_PENDING` 또는 safe
|
||||
non-terminal failure로 닫은 뒤 server reconcile을 재시도한다.
|
||||
- abort 결과가 불명확하면 checkpoint를 제거하지 않고 `ABORT_PENDING`으로 남긴다.
|
||||
- 다음 invocation에서 server status가 `ABORTED/EXPIRED/NOT_FOUND`임을 확인한 뒤
|
||||
checkpoint를 제거한다.
|
||||
- abort endpoint의 HTTP 404/410은 terminal orphan으로 mapping하고 checkpoint를
|
||||
제거한다.
|
||||
- backend TTL cleanup은 비용과 보안 경계다. orphan upload count/bytes/age SLO를
|
||||
운영한다.
|
||||
- multipart complete는 `QUARANTINED`; scanner/CDR와 domain metadata commit이
|
||||
끝나기 전 public delivery capability를 발급하지 않는다.
|
||||
- application-facing success/log에는 session ID, request binding 또는 file
|
||||
fingerprint를 노출하지 않는다.
|
||||
|
||||
## 5. Streaming download
|
||||
|
||||
1. capability media/length/digest와 실제 response header/body count를 비교한다.
|
||||
2. `Content-Encoding`이 capability byte semantics와 다른지 확인한다.
|
||||
3. overrun/truncation/integrity failure면 reader와 destination writable을 abort한다.
|
||||
4. partial local destination을 성공으로 보고하지 않는다.
|
||||
5. same resource를 새 capability와 새 destination으로 restart한다.
|
||||
6. Range resume가 별도 승인되지 않았으면 기존 partial destination에 append하지
|
||||
않는다.
|
||||
7. anchor/navigation 결과는 browser handoff일 뿐 saved/verified 증거가 아니다.
|
||||
|
||||
### 5.1 Range resumable download
|
||||
|
||||
이 절차는 `RANGE_RESUMABLE_DOWNLOAD_V1` port/runtime, seekable destination 또는
|
||||
owned OPFS staging과 provider contract가 구현·조합된 경우에만 적용한다.
|
||||
|
||||
1. checkpoint의 protocol, opaque account partition, representation binding,
|
||||
total bytes, next offset, destination binding, revision과 retention을
|
||||
검증한다. raw URL, signed header, validator, file path는 checkpoint나
|
||||
incident ticket에 없어야 한다.
|
||||
2. destination의 실제 durable length가 checkpoint offset과 다르면 append하지
|
||||
않는다. 더 긴 uncommitted tail은 정책과 destination capability가 허용할 때만
|
||||
`truncate()`하고, 더 짧으면 마지막 confirmed segment로 rollback하거나
|
||||
새 staging으로 재시작한다.
|
||||
3. 새 capability가 같은 immutable generation/strong validator, total length,
|
||||
media type와 whole-object digest에 묶였는지 확인한다. capability의
|
||||
`preconditionMode`, `allowWholeObjectFallback`과 exact `allowedStatuses`도
|
||||
확인하며 허용되지 않은 status에서는 body나 destination을 소비하지 않는다.
|
||||
4. 허용된 `206`은 exact `Content-Range: bytes S-E/T`, `S=requestedStart`,
|
||||
`E+1=requestedEndExclusive`, `Content-Length=E-S+1`, identity encoding과
|
||||
capability precondition binding이 모두 맞을 때만 segment를 쓴다.
|
||||
5. 허용된 full `200`은 response validator/generation evidence가 capability
|
||||
representation binding과 같을 때만 평가한다. mismatch면 body를 쓰지 않고
|
||||
staging/checkpoint를 quarantine한 뒤 새 representation으로 restart한다.
|
||||
binding이 같아도 start가 0이고 whole-object fallback이 허용된 경우만 fresh
|
||||
destination에서 consume하며, 그 밖에는 body를 쓰지 않고 Range reissue,
|
||||
byte-0 restart, handoff 또는 unsupported로 닫는다. 일반 `If-Range` mismatch의
|
||||
표준 응답은 full `200`이지 `412`가 아니다.
|
||||
6. 허용된 `412`는 별도 `If-Match` 또는 provider generation precondition을 쓴
|
||||
계약에서만 representation replacement로 처리한다.
|
||||
7. 허용된 `416`은 checkpoint 완료 증거가 아니다. body를 data로 소비하지 않고,
|
||||
final URL/header와 mode별 validator/generation binding을 먼저 검증한다.
|
||||
provider response만으로 binding을 증명할 수 없으면 BFF reconcile이 exact
|
||||
immutable generation을 증명해야 한다. 그 뒤 server total이 expected total과
|
||||
같은지, `nextOffset <= total`인지, destination length와 offset이 일치하는지를
|
||||
순서대로 reconcile한다. length mismatch는 먼저 truncate/verified rollback
|
||||
또는 quarantine하고 같은 invocation에서 fall through하지 않는다.
|
||||
`length == nextOffset == total`과 처음부터 다시 계산한 whole-object SHA-256이
|
||||
모두 일치할 때만 final commit 후보가 된다. `length == nextOffset < total`이면
|
||||
exact missing range를 재발급하고 반복 `416`은 contract mismatch로 닫는다.
|
||||
8. capability expiry는 같은 representation binding으로 재발급한다. binding이
|
||||
달라지면 기존 partial에 이어 쓰지 않는다.
|
||||
9. segment flush와 destination length 재확인 뒤에만 checkpoint CAS를 전진한다.
|
||||
10. final whole-object SHA-256과 destination close/commit 뒤에만
|
||||
`SAVED_VERIFIED`로 기록한다.
|
||||
|
||||
malformed `Content-Range`, encoded representation drift, validator mismatch,
|
||||
seek/truncate 실패 또는 crash 뒤 ambiguous tail에서는 신규 Range traffic을
|
||||
중지하고 whole-object restart, browser-managed handoff 또는 명시적 unsupported로
|
||||
degrade한다.
|
||||
|
||||
### 5.2 Background download와 browser-managed handoff
|
||||
|
||||
- `BROWSER_HANDOFF`는 브라우저 download manager가 이후 작업을 소유한다는
|
||||
outcome이며 application progress, retry, destination integrity를 증명하지 않는다.
|
||||
- page abort/reload 뒤 checkpoint가 남는 것은 app-managed background download 실행
|
||||
증거가 아니다.
|
||||
- app-managed background download를 선택한 제품만 별도 worker control plane, owned OPFS
|
||||
staging, runtime/version/logout fence와 foreground export 절차를 운영한다.
|
||||
- worker가 종료되거나 capability가 만료되면 checkpoint로 foreground recovery를
|
||||
시도하며 장시간 keepalive를 가정하지 않는다.
|
||||
- 미지원 Safari/Firefox/WebView에서는 browser-managed handoff 또는 명시적
|
||||
unsupported UX를 사용한다. unbounded Blob fallback은 금지한다.
|
||||
|
||||
## 6. Image CDN
|
||||
|
||||
1. 신규 private descriptor 발급 또는 영향 preset을 kill switch로 중지한다.
|
||||
2. 안전한 placeholder/original-approved rendition으로 fallback한다.
|
||||
3. asset revision과 CDN cache key, format, width/height metadata를 확인한다.
|
||||
PNG/JPEG/WebP/AVIF header를 native decode 전에 파싱해 선언 dimensions,
|
||||
pixel/decoded-byte budget과 static-only 조건을 통과했는지 확인한다.
|
||||
versioned preset binding ID를 server registry에서 조회해 요청의
|
||||
width/height/DPR/fit/format/quality 전체를 재계산하고 query mismatch를
|
||||
거절하는지도 확인한다.
|
||||
4. 같은 immutable URL의 content가 변경됐다면 purge만으로 봉합하지 않고 새
|
||||
asset revision을 발급한다.
|
||||
5. signed private URL이 노출됐으면 expiry를 기다리지 말고 backend asset/capability
|
||||
scope를 revoke한다.
|
||||
6. private descriptor가 `PRIMARY_REQUIRED` probe를 사용하고 실제 response에서
|
||||
exact URL, credential omission과 `Cache-Control: no-store`를 확인했는지
|
||||
검사한다.
|
||||
CDN origin이 application origin과 분리되어 실제
|
||||
`<img crossorigin="anonymous">` 요청에도 application cookie가 실리지
|
||||
않는지 배포 설정을 함께 확인한다.
|
||||
7. fetch header/body/decode 전체 timeout과 abort cleanup을 확인한다. 늦게
|
||||
resolve한 response body/`ImageBitmap`이 즉시 cancel/close되는지 fault
|
||||
injection으로 재현한다.
|
||||
8. source-fetch SSRF가 의심되면 arbitrary source URL transform을 차단하고
|
||||
quarantine을 통과한 asset registry ID만 허용한다.
|
||||
9. dimensions/pixel/decoded-byte budget 위반은 CDN transform과 descriptor
|
||||
양쪽을 중지한다.
|
||||
10. 정상 signing key 회전은 verifier와 `acceptedKeyIds`에 old/new key를 먼저
|
||||
함께 배포하고 client 채택을 확인한 뒤 backend signer를 전환한다. 기존
|
||||
capability lifetime, clock skew와 client rollout 기간이 모두 지난 후에만
|
||||
old key를 제거한다.
|
||||
11. key 유출, logout, tenant/account partition 변경 또는 feature teardown이면
|
||||
backend capability를 revoke하고 기존 Image CDN runtime을 `close()`해 진행
|
||||
중 verification/probe와 기존 reference를 폐기한 뒤 새 runtime을 조합한다.
|
||||
12. descriptor HTTP provider를 설치했다면 response가
|
||||
`IMAGE_CDN_DESCRIPTOR_V1`, fixed endpoint, strict body/content-type/deadline과
|
||||
현재 runtime generation을 만족하는지 확인한다.
|
||||
13. refresh는 asset/preset/scope/generation별 single-flight인지, 기존 descriptor
|
||||
expiry 뒤 stale-while-error를 허용하지 않는지 확인한다.
|
||||
14. logout/account switch 뒤 늦게 끝난 descriptor fetch/signature
|
||||
verification/decode가 새 runtime에 채택되지 않는지 확인한다.
|
||||
15. safe picture primitive가 verified presentation descriptor를 그대로
|
||||
projection할 뿐 URL/query/transform을 재조립하지 않는지 확인한다.
|
||||
|
||||
## 7. Kill switch
|
||||
|
||||
- direct object-storage transfer off → same-origin BFF proxy 또는 기능 중지
|
||||
- concurrent multipart → sequential part
|
||||
- resumable off → 승인된 small-file simple upload 또는 upload 중지
|
||||
- complete off → active session 유지/reconcile only
|
||||
- upload pause/resume off → 신규 part 중지, explicit reconcile only
|
||||
- streaming save off → authorized browser handoff
|
||||
- Range resume off → whole-object restart 또는 authorized browser handoff
|
||||
- app-managed background download off → browser-managed handoff 또는 foreground only
|
||||
- app-managed background upload off → foreground checkpoint resume 또는 upload 중지
|
||||
- Image descriptor refresh off → fresh descriptor expiry까지만 사용 후 placeholder
|
||||
- private Image CDN off → authenticated same-origin placeholder
|
||||
- advanced formats off → approved JPEG/PNG preset
|
||||
- responsive candidates off → 한 개의 bounded fallback rendition
|
||||
|
||||
kill switch는 hard byte/pixel/security ceiling을 늘리지 않는다.
|
||||
|
||||
## 8. 복구 완료 조건
|
||||
|
||||
- capability expiry/replay/CORS 실패율이 baseline으로 복귀
|
||||
- 신규·resume upload의 part checksum과 ordered complete 증적 통과
|
||||
- orphan TTL cleanup과 quarantine backlog가 SLO 안으로 복귀
|
||||
- download truncation/overrun/integrity fault injection 통과
|
||||
- Range를 선택한 경우 `200/206/412/416`, capability reissue, destination
|
||||
seek/truncate/crash와 final whole-object integrity matrix 통과
|
||||
- pause/inventory를 구현한 경우 abandoned checkpoint retention/reconcile drill 통과
|
||||
- image preset/revision/cache/CSP matrix 통과
|
||||
- Image descriptor provider를 설치한 경우 expiry/refresh/logout generation
|
||||
fence와 safe projection 통과
|
||||
- image pre-decode static metadata, private no-store와 timeout cleanup fault 통과
|
||||
- Chromium/Firefox/WebKit 동일 case set, zero failure/skipped
|
||||
- raw URL/query/signed header/bearer token/capability가 log, telemetry,
|
||||
checkpoint에 없음을 확인
|
||||
- checkpoint allowlist의 SHA-256 fingerprint/part checksum/opaque receipt가
|
||||
account partition과 retention 안에만 있고 log/telemetry에는 없음을 확인
|
||||
|
||||
## 9. 제거
|
||||
|
||||
1. 신규 capability/session/descriptor 발급 중지
|
||||
2. active upload complete 또는 explicit abort와 orphan cleanup
|
||||
3. non-secret checkpoint를 scope에 맞게 purge
|
||||
4. private capability revoke와 CDN grace window drain
|
||||
5. feature transfer/image facade와 composition 제거
|
||||
6. browser-transfer source와 전용 tests/catalog wiring 제거
|
||||
7. runtime removal gate, production module inventory, 전체 test/build 재검증
|
||||
|
||||
## 10. 관련 문서
|
||||
|
||||
- [Browser data capability completion ledger](../architecture/browser-data-capability-completion-ledger.md)
|
||||
- [Presigned transfer and Image CDN](../architecture/presigned-transfer-and-image-cdn.md)
|
||||
- [VD-14 Resumable download와 background download](../architecture/decisions/VD-14-resumable-download-and-background-transfer.md)
|
||||
- [VD-16 Browser transfer composition과 Image delivery](../architecture/decisions/VD-16-browser-transfer-composition-and-image-delivery.md)
|
||||
- [Server file capability infrastructure](../architecture/server-file-capability-infrastructure.md)
|
||||
Reference in New Issue
Block a user