feat: add signed SABR compatibility profiles - #89
Conversation
|
Thanks for the substantial work on both this PR and PipePipeClient#78, especially the detailed implementation and validation. After thinking this through more carefully, I believe the direction I originally suggested may not be the right one. Supporting arbitrary SABR changes would require a much larger redesign than this abstraction can reasonably cover. On the other hand, if we limit the scope to field changes and simple policies, the information collected recently suggests that such changes are not as frequent as I initially expected. With that in mind, I don't think these PRs are the right path forward, so I plan to close both without merging. For now, I would prefer to focus on fixing the remaining bugs in the current SABR implementation. |
|
Yep np at all, I understand ur point :) And yeah I agree, if these field changes and small policies are not happening that often, keeping this whole compatibility layer would probably add more maintenance than it saves. Focusing on the remaining bugs makes more sense ! |
Summary
This is my idea of how we can deal with future SABR changes without having to release a new Extractor and app version every time YouTube moves a field or changes a small part of the protocol.
I first looked at extending the JavaScript policy work, but I think it gives the downloaded policy the wrong shape: either the runtime is privileged enough to duplicate a large part of the native SABR engine, or it is restricted enough that every useful change still needs a new bridge API and a release.
This PR replaces that path with signed declarative compatibility profiles. A profile describes the volatile WEB and MWEB parts of SABR while the Extractor keeps transport, tokens, UMP framing, media assembly, state transitions and limits in native code.
This is the Extractor half of the first complete implementation. The Android delivery and persistence lifecycle is handled separately in PipePipe Client.
Architecture
SabrCompatibilityProfileParseraccepts a strict UTF-8 document with a 1 MiB size limit, bounded nesting and no unknown properties.SabrCompatibilityProfileValidatorchecks every client, request field, response mapping, recovery rule and capability before a profile can become active.SabrCompatibilityProfileManagerverifies Ed25519 signatures against an identified embedded public key. The signature coversSabrCompatibilityProfile.serialize(), a deterministic binary representation that uses stable string identifiers instead of Java enum ordinals. It also checks the validity interval, document format, minimum Extractor revision, supported capabilities and a monotonic positive revision.The active profile is immutable.
CompatibilitySabrSessionPolicyselects WEB or MWEB once when the session receives its first request. A document can cover only one client: the omitted client continues usingBuiltinSabrSessionPolicywithout invalidating the document.What a profile can change
Request templates are separate for the initial and following SABR requests. Each entry selects a protobuf field number, wire type, required flag and one native value source:
The profile receives only the already encoded value selected by the native host. It cannot access the underlying cookie, token or mutable playback state directly. Generated requests remain under the native 256 KiB limit.
Response mappings associate a UMP part type and a bounded protobuf field path with a normalized native target. This covers:
Media payload bytes are never exposed to the profile. They continue through the existing streaming collector and cache path.
Recovery remains declarative as well. A rule combines known predicates with known actions and must end in one terminal action. There are no loops or arbitrary callbacks, and omission, elapsed-time, forward-seek and retry values stay inside native bounds.
Native ownership
The profile is data, not a second SABR implementation.
YoutubeSabrSessionstill owns:SabrSessionPolicyHostvalidates decisions again before committing state. A profile cannot perform network requests, execute code, inspect process state, allocate unbounded data or introduce a new action that the Extractor does not already understand.Failure handling
FallbackSabrSessionPolicyis a session-local circuit breaker around the profiled policy. If request construction, response interpretation or demand routing fails, the session disables that profile and falls back to the bundled policy.Media framing is selected once for a response. If a profiled media-header decode fails, the profile is disabled and the failure remains recoverable so the native session can retry without mixing two framing contracts inside the same response.
The bundled behavior therefore remains both the default when no valid profile is supplied and the recovery path when a signed profile is incompatible with a real response.
Changes
SabrSessionPolicyYoutubeSabrSessiontransport and state machineCompatibility boundary
This can handle changes to known request fields, response field paths, protobuf wire types, UMP media part numbers, recovery thresholds and combinations of existing native actions.
It cannot introduce a new transport, cryptographic primitive, token flow, media framing model or host capability. Those are structural changes and still require an Extractor release. The goal is to cover the frequent protocol-layout changes without pretending every possible YouTube change can be represented as configuration.
Client integration
The PipePipe Client change handles profile delivery, persistence, revision floors and activation. This Extractor PR only defines the signed document contract, its native evaluator and the session fallback boundary.
The complete download, cache, stable/beta channel and rollback lifecycle will be documented in the Client PR. Without a valid profile supplied by a caller, the Extractor continues using the bundled builtin policy.
Validation
./gradlew --no-daemon checkgit diff --checkDelivery status
This PR does not publish a profile service, signing key or production document. It only adds the format and native execution boundary. Remote delivery stays disabled unless a caller explicitly provides trusted keys and a valid signed profile.
Related PR