Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions docs/APP-STORE-SCREENSHOTS.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
# App Store screenshot automation

Labstream's App Store capture workflow uses the real native shells with the Debug-only synthetic
The existing **placeholder/layout-validation** capture workflow uses the real native shells with the Debug-only synthetic
browse catalog. It does not authenticate, contact a media server, or read persisted accounts. Raw
runner evidence remains ignored; the export contains only JPEGs and a sanitized checksum manifest.

## Apple requirements checked on 2026-08-21
Final release images must instead use the controlled licensed catalog and ordinary app sessions;
see the [licensed-media capture plan](https://github.com/jlipworth/Labstream/blob/main/docs/plans/2026-09-15-store-demo-capture.md). The fixture exporter
does not authenticate to that catalog and a `store-ready` folder name is not release approval.

## Apple requirements checked on 2026-09-29

Apple allows one through ten screenshots per device size in PNG, JPEG, or JPG and forbids alpha
channels. Apple's current accepted capture sizes relevant to the preferred Labstream lanes are:

| Product | Preferred store export | Other accepted sizes used by fallback checks |
| --- | --- | --- |
| Apple Vision Pro | 3840 × 2160 | None |
| iPhone | 1206 × 2622 portrait (iPhone 17/17 Pro lane) | The checked manifest also includes Apple's current 6.9-, 6.5-, 6.3-, and 6.1-inch portrait sizes |
| iPhone | Final required lane: 1320 × 2868 portrait (6.9-inch); existing fixture lane: 1206 × 2622 (6.3-inch) | The checked manifest also includes Apple's current 6.9-, 6.5-, 6.3-, and 6.1-inch portrait sizes |
| iPad | 2064 × 2752 portrait (13-inch M5/M4 lane) | 2048 × 2732 and current 11-inch portrait sizes |
| Apple TV | 3840 × 2160 | 1920 × 1080 |
| Mac | 2560 × 1600 | 1280 × 800, 1440 × 900, and 2880 × 1800 |

A 6.3-inch capture alone does **not** satisfy the required iPhone set: supply a 6.9-inch
set or the 6.5-inch fallback. Likewise, use the required 13-inch iPad set, not only 11-inch.
The exporter checks accepted dimensions, not required display-class coverage.

The machine-readable checked set and verification date live in
`scripts/app-store-screenshot-specs.json`. Recheck it before each release against Apple's
[screenshot specifications](https://developer.apple.com/help/app-store-connect/reference/app-information/screenshot-specifications)
Expand Down
23 changes: 21 additions & 2 deletions docs/RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ parentheses is the App Store build number (`CFBundleVersion`). A new marketing v
build 1; each replacement upload for the same platform and marketing version must use a higher
build number.

## Current release audit and deferred scope

The [2026-09-15 disclosure audit](https://github.com/jlipworth/Labstream/blob/main/docs/evidence/2026-09-15-prerelease-disclosure-audit.md)
is a historical source audit, not current account certification. Its wording and required-reason
API fixes landed in PRs #329 and #328. Owner-approved disclosure mapping remains open in
[#325](https://github.com/jlipworth/Labstream/issues/325), and exact signed-archive privacy reports
remain open in [#326](https://github.com/jlipworth/Labstream/issues/326).

[#259](https://github.com/jlipworth/Labstream/issues/259), disconnected cold-launch access to
completed downloads, is **deferred by user decision**. Keep the issue open; deferral is not
hardware acceptance. Do not advertise reliable offline cold launch as verified.

The [licensed-media capture plan](https://github.com/jlipworth/Labstream/blob/main/docs/plans/2026-09-15-store-demo-capture.md)
retains final screenshots, storefront attribution, age suitability and clean-install reviewer
journeys as open gates. Recorded provenance is not a fresh deployed-catalog hash check.

## Coordinated publication sequence

For a new coordinated archive set or the first public release, use this order. If the neutral
Expand Down Expand Up @@ -176,8 +192,11 @@ and App Store Connect state before relying on them for a new submission.
proposed data-type mapping and owner approval gates. No App Store Connect answer is changed
by documentation edits. Required-reason API declarations are a separate packaging check
([#326](https://github.com/jlipworth/Labstream/issues/326)), not a collection exemption.
- [x] Export-compliance answers match the binary. `ITSAppUsesNonExemptEncryption` is currently
false; re-evaluate if cryptography use changes.
- [ ] Verify export-compliance answers against the exact candidate and linked dependencies.
`ITSAppUsesNonExemptEncryption` remains false in source; this is not a new binary/account
certification.
- [ ] Evaluate any storefront accessibility feature claims on each submitted platform against
Apple's criteria; source labels and passing tests alone do not establish support.

- [ ] Inspect app/dependency privacy manifests and the Xcode aggregate privacy report from
each exact signed Release archive (visionOS, iOS/iPadOS, tvOS, macOS). Confirm the
Expand Down
120 changes: 120 additions & 0 deletions docs/app-review-media-provenance.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
{
"schemaVersion": 1,
"catalogRelease": "open-films-v1",
"verifiedOn": "2026-09-15",
"sourceManifestSha256": "7954af871fcef8d1f02bf29445257ee3236d791e5c7b2eb720ce6201cd5974fb",
"scope": "Public source and derivative identities copied from the deployed review catalog manifest; not a fresh live-server checksum audit.",
"openFilms": [
{
"title": "Spring",
"year": 2019,
"copyrightHolder": "Blender Foundation",
"projectUrl": "https://studio.blender.org/projects/spring/pages/about/",
"license": {
"name": "Creative Commons Attribution 4.0 International",
"spdx": "CC-BY-4.0",
"url": "https://creativecommons.org/licenses/by/4.0/",
"officialPermissionPage": "https://studio.blender.org/projects/spring/pages/about/",
"commercialReuseExplicitlyPermitted": true,
"requiredAttribution": "\u00a9 Blender Foundation | cloud.blender.org/spring",
"excludedMaterial": "Logos, including the Blender, Spring, and Creative Commons logos, associated trademarks, and clearly third-party material are excluded by the official project page. No logo or title design is used as catalog artwork.",
"verifiedOn": "2026-09-29"
},
"source": {
"sourcePage": "https://video.blender.org/w/8B5QmoLSS3mWJ4fmZZu3Ye",
"downloadUrl": "https://video.blender.org/download/web-videos/3d95fb3d-c866-42c8-9db1-fe82f48ccb95-804.mp4",
"sha256": "9d11691b30a9fd192205224e9b0b59f918f33043ae600a791c9c6c1909d42309",
"bytes": 110388897,
"container": "MP4",
"video": "H.264 1920x804 24fps",
"audio": "AAC",
"durationSeconds": 464.167007,
"authoritativeHost": "video.blender.org",
"verifiedOn": "2026-08-22"
},
"playable": {
"relativePath": "Movies/Spring (2019)/Spring (2019).mp4",
"transform": "None; the complete authoritative encode, including its end credits, is preserved byte-for-byte.",
"sha256": "9d11691b30a9fd192205224e9b0b59f918f33043ae600a791c9c6c1909d42309",
"fullEndCreditsPreserved": true
},
"derivedArtwork": [
{
"relativePath": "Movies/Spring (2019)/poster.jpg",
"basis": "Frame from the licensed complete film at 180 seconds; no title card or logo.",
"transform": "Pinned Jellyfin FFmpeg 7.1.4: crop 536x804 at x=950,y=0; Lanczos scale to 600x900; JPEG q=2; metadata removed; single-threaded.",
"sha256": "7b3b064d86d05e1fbe78c0dcdbd83e81bb29eec8d3335077e23026bc0aa53326"
},
{
"relativePath": "Movies/Spring (2019)/backdrop.jpg",
"basis": "Frame from the licensed complete film at 180 seconds; no title card or logo.",
"transform": "Pinned Jellyfin FFmpeg 7.1.4: crop 1429x804 at x=400,y=0; Lanczos scale to 1280x720; JPEG q=2; metadata removed; single-threaded.",
"sha256": "3dc0be7badd063463ea5e43e1b5992fd8c7d7f5e1db1510c487ed088f6393253"
}
]
},
{
"title": "Wing It!",
"year": 2023,
"copyrightHolder": "Blender Foundation",
"projectUrl": "https://studio.blender.org/projects/wing-it/",
"license": {
"name": "Creative Commons Attribution 4.0 International",
"spdx": "CC-BY-4.0",
"url": "https://creativecommons.org/licenses/by/4.0/",
"officialPermissionPage": "https://studio.blender.org/projects/wing-it/pages/licensing/",
"commercialReuseExplicitlyPermitted": true,
"requiredAttribution": "(CC) Blender Foundation | studio.blender.org",
"excludedMaterial": "Logos, including the Blender, Wing It!, Creative Commons, and sponsor logos, associated trademarks, and clearly third-party material are excluded by the official project page. No logo or title design is used as catalog artwork.",
"verifiedOn": "2026-09-29"
},
"source": {
"sourcePage": "https://video.blender.org/w/vrjS4NB4cwnJhrib8uEwi6",
"downloadUrl": "https://video.blender.org/download/web-videos/ee623d80-fd8d-4a2a-8aae-1f5acf796f27-1080.mp4",
"sha256": "fdb3666b1f9a738c9fcb8e251286b17fb502cd3880bb43ac3f5868a907f71e4e",
"bytes": 48136527,
"container": "MP4",
"video": "H.264 1920x1080 24fps",
"audio": "AAC",
"durationSeconds": 237.941,
"authoritativeHost": "video.blender.org",
"verifiedOn": "2026-08-22"
},
"playable": {
"relativePath": "Movies/Wing It! (2023)/Wing It! (2023).mp4",
"transform": "None; the complete authoritative encode, including its end credits, is preserved byte-for-byte.",
"sha256": "fdb3666b1f9a738c9fcb8e251286b17fb502cd3880bb43ac3f5868a907f71e4e",
"fullEndCreditsPreserved": true
},
"derivedArtwork": [
{
"relativePath": "Movies/Wing It! (2023)/poster.jpg",
"basis": "Frame from the licensed complete film at 150 seconds; no title card or logo.",
"transform": "Pinned Jellyfin FFmpeg 7.1.4: crop 720x1080 at x=300,y=0; Lanczos scale to 600x900; JPEG q=2; metadata removed; single-threaded.",
"sha256": "82692ecdf809128778e95f4344cf106fc23cdef829eecbf1b220d2e71b888dc5"
},
{
"relativePath": "Movies/Wing It! (2023)/backdrop.jpg",
"basis": "Frame from the licensed complete film at 150 seconds; no title card or logo.",
"transform": "Pinned Jellyfin FFmpeg 7.1.4: Lanczos scale to 1280x720; JPEG q=2; metadata removed; single-threaded.",
"sha256": "b3e79dec0aab6f553f5ba948083acfe87b1e418e69169ad4fad837cd60954582"
}
]
}
],
"proceduralTechnicalFixtures": {
"generator": {
"path": "files/generate-media.sh",
"runtime": "jellyfin/jellyfin:10.11.11",
"runtimeDigest": "sha256:aefb67e6a7ff1debdd154a78a7bbb780fd0c873d8639210a7f6a2016ad2b35db",
"sourceSha256": "c6d691f8c40c7acac7627d2f511710f17b72ab9fba97c4ae117714c55fbf410d",
"deterministicInputs": true
},
"origin": "Repository-authored procedural gradients, synthesized audio, subtitles, chapters, and plain fictional technical metadata; no third-party material is incorporated.",
"rightsHolder": "Repository owner",
"license": "Project-Owned-Original-App-Review-Use",
"role": "Secondary codec, subtitle, chapter, seek, audio, and download fixtures. The CC BY open films are the visually dominant Movies catalog."
},
"noEndorsement": "Labstream is not affiliated with or endorsed by Blender Foundation, Blender Studio, or the film creators. Names appear only to identify and attribute the licensed works.",
"storefrontGate": "Every visible asset must match this allowlist, include attribution, and pass editorial/age-suitability review. Final captures are not yet produced."
}
68 changes: 68 additions & 0 deletions docs/evidence/2026-09-15-prerelease-disclosure-audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Pre-release disclosure audit — 2026-09-15

Scope: read-only source audit at merged `e9344f63`, public support/privacy reachability,
release tracking, and review of screenshot branch `7f93e1ee`. This is not a fresh App Store
Connect query, legal certification, signed-archive inspection, or platform acceptance run.
No account answers, runtime code, privacy manifest, server configuration, or release settings
were changed by this audit. Follow-up fixes remain separate.

## Subsequent disposition — 2026-09-29

The findings below preserve the original audit. PR #328 subsequently added the required-reason
uptime declaration; PR #329 corrected the disclosure wording. Issues #325 and #326 remain open
for owner-approved disclosure mapping and exact signed-archive privacy reports respectively.
PR #327 also landed and closed #323 under its client-handling scope; it does not certify the
broader codec sweep or server worker cleanup. Consult [current release gates](../RELEASES.md)
rather than treating the historical statuses below as today's task list.

## Findings and disposition

| Area | Source evidence | Disposition |
| --- | --- | --- |
| Required-reason APIs | `PrivacyInfo.xcprivacy` declares UserDefaults, FileTimestamp and DiskSpace, but production `PlaybackController` and `PlaybackDiagnostics` use `systemUptime`. | Missing SystemBootTime declaration: [#326](https://github.com/jlipworth/Labstream/issues/326). Audit uses/egress and inspect all final archive manifests. |
| Developer-operated services | `PRIVACY.md` says no developer-operated backend; dedicated review/demo infrastructure is tracked in #280. | Reconcile the absolute claim and actual service retention: [#325](https://github.com/jlipworth/Labstream/issues/325). Do not invent retention or silently certify Data Not Collected. |
| Optional feedback | Policy describes user-initiated redacted GitHub/report exports. | Check all optional-disclosure conditions, not user initiation alone, in #325. |
| Permission descriptions | Shared plist names Plex/Jellyfin/Emby and browse/play/download; tvOS plist separately omits download. Both declare scoped local networking, not arbitrary-load ATS. | Source wording matches platform feature scope; final compiled plist and real permission UX still require acceptance. |
| Export answer | Both app plists set non-exempt encryption false. Source uses Apple networking and CryptoKit SHA-256; PMSKit pins swift-crypto with swift-asn1 resolved. | No flag change in this audit. Inspect actual linked Release dependencies and reconcile the existing answer; a source search is not export certification. |
| Public links | HTTPS support and privacy endpoints both returned HTTP 200. | Reachability checked; this does not establish policy completeness. |
| Accessibility | Source/test coverage is not a completed platform-specific accessibility evaluation. | Keep storefront feature claims unapproved until evaluated against the exact candidate. |
| Source versus distribution | Main is 1.7.1 build 1; latest recorded processed set is 1.6.1 build 2. | Preserve this distinction; do not mark current binary gates complete from historical checks. |

Apple lists `systemUptime` under SystemBootTime. Reason 35F9.1 covers in-app elapsed-time/timer
uses with restrictions on off-device information; inspect the actual use before selecting it.
See [required-reason API categories](https://developer.apple.com/documentation/bundleresources/app-privacy-configuration/nsprivacyaccessedapitypes/nsprivacyaccessedapitype).

Apple's [privacy guidance](https://developer.apple.com/app-store/app-privacy-details/) requires
current answers covering applicable collection, including functionality-only uses. Optional
support disclosures must satisfy all listed conditions. The empty collection array in the
manifest does not independently establish a correct storefront privacy label.

Use Apple's [accessibility evaluation criteria](https://developer.apple.com/help/app-store-connect/manage-app-accessibility/overview-of-accessibility-nutrition-labels/)
for each submitted platform; no accessibility support claim was approved by this audit.

## Screenshot branch review

Reviewed `7f93e1ee` on `codex/store-demo-provenance`; no merge performed. Its changes separate
synthetic fixture/layout images from real final captures, document licensed media and derivative
artwork provenance, and retain review/attribution and age-suitability gates. Nine screenshot
tooling tests passed during this review. This is a documentation/tooling review, not fresh live
catalog hash verification or legal clearance of every eventual screenshot.

The plan correctly retains the large-iPhone and large-iPad requirements instead of treating any
accepted image dimensions as sufficient coverage. The existing exporter validates dimensions,
not completeness of required display classes. Confirm final sets against Apple's
[screenshot specifications](https://developer.apple.com/help/app-store-connect/reference/app-information/screenshot-specifications).
Actual captures, normal review-account login, attribution presentation, editorial age-rating
review and exact TestFlight journeys remain outstanding. Do not equate openly licensed media
with automatic suitability for a 4+ presentation.

## Release tracking

- #259 is explicitly deferred by the user; it remains open and unvalidated. Deferral is not a
disconnected cold-launch pass or a promise of reliable offline cold launch.
- #280 has bounded infrastructure evidence, but exact-candidate app/reviewer acceptance remains open.
- Playback investigations #288, #291, #303, #305, #322, #323 and #324 are not blanket-closed by
merged implementation work, isolated successful runs, or this audit.
- #325 and #326 require resolution before certifying current privacy disclosures/packaging.
- App Store Connect versions, agreements, trader status, export answers and accessibility labels
were not queried or modified; those remain a separate read-only account audit and approval gate.
Loading