Withdraw the file-hash tier; document the legal posture
Removes `cut.video_hash` and the `exact` match tier on legal grounds. The OpenSubtitles hash was the strongest technical signal available — it identifies a specific file, so it cannot produce a false positive — and that is exactly the problem. Every tier must be a claim about a *cut*, never about a copy. A TMDB id discloses "some copy of this film", which is what a library catalogue discloses. A file hash discloses "this exact release": it made a read endpoint into a release-level oracle, and made an instance's database a mapping from file fingerprints to the instances holding them. That is a far more specific disclosure than PR-005 permits, and a dataset no volunteer operator should be asked to hold. The audio signature is the replacement: derived from content, it identifies the cut rather than the copy, so two encodes of the same edit agree. The field is deleted rather than kept as a vestigial null, on the same reasoning §2 applied to `anneal_sec` — a key naming a signal the format no longer has is actively misleading — so an upload carrying one is now an unknown-field 400, with a test asserting it. **Every content_id changes**, including for manifests that never carried a hash, because the canonical `cut` object lost a key. The golden vector is regenerated and re-verified against an independent Python implementation; the plugin and extraction repos must adopt the new value or federation deduplication silently breaks. Free now, pre-release; not free later. Adds docs/legal-posture.md, the operator-facing half of what §5a asks for: what an instance holds exhaustively, what it structurally cannot do, and how that sits against the intermediary-liability regimes that plausibly apply. 208 tests. Coverage 25/32. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> TRACES: UR-011 | SR-004, PR-005
This commit is contained in:
+2
-2
@@ -154,7 +154,7 @@ fn movie_manifest(tmdb_id: &str, runtime: f64) -> Value {
|
||||
"jmanifest_version": 2,
|
||||
"identity": { "type": "movie", "tmdb_id": tmdb_id, "title": "The Death of Stalin",
|
||||
"year": 2017 },
|
||||
"cut": { "runtime_sec": runtime, "video_hash": "opensubtitles:8e245d9679d31e12" },
|
||||
"cut": { "runtime_sec": runtime },
|
||||
"extraction": { "sample_fps": 5, "extinction_sec": 12,
|
||||
"pipeline_version": "scene-actor-extraction 0.4.1",
|
||||
"gallery_scope": "global" },
|
||||
@@ -405,7 +405,7 @@ async fn missing_runtime_is_rejected() {
|
||||
let s = TestServer::new("no-runtime");
|
||||
let token = s.token().await;
|
||||
let mut m = movie_manifest("504172", 6420.5);
|
||||
m["cut"] = json!({ "video_hash": "opensubtitles:8e245d9679d31e12" });
|
||||
m["cut"] = json!({ "container_duration_sec": 6420.5 });
|
||||
let (status, _, _) = s.post_json_auth("/api/v1/manifests", &token, &m).await;
|
||||
assert_eq!(status, StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
@@ -138,7 +138,6 @@ impl TestServer {
|
||||
season: None,
|
||||
episode: None,
|
||||
runtime_sec: 6420.5,
|
||||
video_hash: None,
|
||||
audio_signature: None,
|
||||
audio_sig_coarse: None,
|
||||
sample_fps: Some(5.0),
|
||||
@@ -256,7 +255,6 @@ async fn a_pending_manifest_is_not_replicated() {
|
||||
season: None,
|
||||
episode: None,
|
||||
runtime_sec: 100.0,
|
||||
video_hash: None,
|
||||
audio_signature: None,
|
||||
audio_sig_coarse: None,
|
||||
sample_fps: None,
|
||||
|
||||
+1
-1
@@ -210,7 +210,7 @@ async fn sql_payloads_in_query_parameters_are_inert() {
|
||||
format!("/api/v1/manifests/movie?imdb_id={enc}"),
|
||||
format!("/api/v1/manifests/episode?series_tmdb_id={enc}&season=1&episode=1"),
|
||||
format!("/api/v1/manifests/series/{enc}"),
|
||||
format!("/api/v1/manifests/exists?tmdb_id=1&video_hash={enc}"),
|
||||
format!("/api/v1/manifests/exists?tmdb_id=1&runtime_sec={enc}"),
|
||||
] {
|
||||
let (status, body) = s.get(&uri).await;
|
||||
// The payload is bound as data, so it matches nothing. What must never
|
||||
|
||||
Reference in New Issue
Block a user