feat(licence): contributed manifests are CC0 1.0

Contributed manifests were in no declared condition at all, which left §9a
replication with no grant flowing through it: peers mirror each other's
catalogues wholesale, and every hop of that was unlicensed.

CC0 rather than a share-alike licence, because a share-alike works by asserting
a right in the data and then conditioning its use. The position in
docs/legal-posture.md §3 is that presence timings are facts rather than
protectable expression — asserting copyright in them in order to license them
would contradict that argument in the same repository, and that contradiction is
worth more to an opponent than the licence is worth to us. CC0 also waives the
sui generis database right by name, closing the EU-specific residual exposure
from the contributor's side.

The grant is taken at token issuance, and that is not incidental. There are no
accounts, so there is no sign-up to attach terms to, and a manifest arrives over
POST /manifests with no channel to negotiate over. Acquiring the contribute
capability is the only moment a grant can be made, so POST /tokens now returns
the licence and its terms alongside the token — a licence the server publishes
but never delivers is one no contributor agreed to.

The test pins the scope limit as well as the identifier. Bounding the grant to
the manifest is the half that can fail silently: a reworded term reading onto
the underlying work would purport to grant what no contributor can.

Also records the settled code-licence position across all four repositories in
the legal posture, correcting an earlier claim there that the plugin and
extraction repos declared nothing. Both already carried LICENSE files.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

TRACES: UR-019 | PR-006
This commit is contained in:
2026-07-31 10:43:43 +02:00
co-authored by Claude Opus 5
parent d5dd8113ef
commit 4afa36e7a2
8 changed files with 298 additions and 28 deletions
+17 -9
View File
@@ -3,7 +3,7 @@
<!-- GENERATED FILE - do not edit by hand. -->
<!-- Regenerate: scripts/traceability/traceability-gate.sh -->
**Generated:** 2026-07-31T07:49:04+00:00
**Generated:** 2026-07-31T08:06:49+00:00
Denominators are read from [`requirements.md`](requirements.md) at run time, never hardcoded. Coverage counts a requirement only when it is tagged in source **and** has a verification tier this repo's CI host can execute (`T1, T2, static`).
@@ -14,10 +14,10 @@ Denominators are read from [`requirements.md`](requirements.md) at run time, nev
| Source files scanned | 29 |
| TRACES tags found | 43 |
| EXCEPTION tags found | 0 |
| Requirements defined | 32 |
| Requirements covered | 25 |
| **Coverage** | **78.1%** (25/32) |
| Coverage of CI-executable scope | 78.1% (25/32) |
| Requirements defined | 33 |
| Requirements covered | 26 |
| **Coverage** | **78.8%** (26/33) |
| Coverage of CI-executable scope | 78.8% (26/33) |
| Tagged but unexecuted in CI | 0 |
| Orphan tags | 0 |
@@ -25,7 +25,7 @@ Denominators are read from [`requirements.md`](requirements.md) at run time, nev
| Type | Covered | Tagged but unexecuted | Defined |
|---|---|---|---|
| UR | 15 | 0 | 18 |
| UR | 16 | 0 | 19 |
| DR | 10 | 0 | 14 |
- **PR** tags present (separate taxonomy, not counted in coverage): PR-004, PR-005, PR-006
@@ -77,6 +77,7 @@ _None._
| UR-016 | Done | T2 | SR-003 | untagged | - | Accept and store `extraction.gallery_scope`; rank on it (§7) |
| UR-017 | Done | T1, T2 | SR-003 | covered | `src/model.rs` | Accept per-window belief and identification route; `scenes` are objec… |
| UR-018 | Done | T1 | SR-003 | untagged | - | Exclude belief and route from `content_id`, replicating them as attri… |
| UR-019 | Done | T2 | PR-006 | covered | `src/api/upload.rs` | Contributed manifests are CC0 1.0; the grant is delivered with the to… |
| DR-001 | Done | T1 | SR-004 | untagged | - | Strict parse boundary: unknown fields rejected structurally, not by v… |
| DR-002 | Done | unset | SR-004 | covered | `src/api/fetch.rs`, `src/db/repo.rs` | Fully relational storage — no JSON blob on the write path |
| DR-003 | Done | T1 | PR-004 | covered | `src/db/mod.rs` | Single serialized writer connection, with a read pool alongside |
@@ -178,7 +179,7 @@ _None._
### PR-006
**Locations:** 9
**Locations:** 10
- [`src/api/federation.rs:66`](../src/api/federation.rs#L66) — `pub async fn get_changes(`
- [`src/api/federation.rs:108`](../src/api/federation.rs#L108) — `pub async fn get_manifest_by_content_id(`
@@ -186,6 +187,7 @@ _None._
- [`src/api/fetch.rs:127`](../src/api/fetch.rs#L127) — `pub async fn get_series(`
- [`src/api/upload.rs:29`](../src/api/upload.rs#L29) — `pub async fn post_manifest(`
- [`src/api/upload.rs:101`](../src/api/upload.rs#L101) — `pub async fn post_bundle(`
- [`src/api/upload.rs:245`](../src/api/upload.rs#L245) — `pub async fn post_token(`
- [`src/ingest.rs:50`](../src/ingest.rs#L50) — `pub fn persist(`
- [`src/validate.rs:572`](../src/validate.rs#L572) — `pub fn validate_bundle_envelope(b: &SeriesBundle) -> VResult<()>`
- [`src/worker.rs:107`](../src/worker.rs#L107) — `async fn run_federation_pull(&self, payload: &str) -> Result<(), JobError>`
@@ -233,7 +235,7 @@ _None._
- [`src/api/report.rs:56`](../src/api/report.rs#L56) — `pub async fn post_report(`
- [`src/api/upload.rs:29`](../src/api/upload.rs#L29) — `pub async fn post_manifest(`
- [`src/api/upload.rs:223`](../src/api/upload.rs#L223) — `pub async fn post_token(`
- [`src/api/upload.rs:245`](../src/api/upload.rs#L245) — `pub async fn post_token(`
- [`src/app.rs:26`](../src/app.rs#L26) — `pub fn router(state: AppState) -> Router`
- [`src/auth.rs:22`](../src/auth.rs#L22) — `pub fn hash_token(token: &str) -> String`
- [`src/auth.rs:76`](../src/auth.rs#L76) — `pub fn client_ip(headers: &HeaderMap, peer: Option<IpAddr>, trusted_proxies: &[IpAddr]) -…`
@@ -293,7 +295,7 @@ _None._
**Locations:** 6
- [`src/api/report.rs:56`](../src/api/report.rs#L56) — `pub async fn post_report(`
- [`src/api/upload.rs:223`](../src/api/upload.rs#L223) — `pub async fn post_token(`
- [`src/api/upload.rs:245`](../src/api/upload.rs#L245) — `pub async fn post_token(`
- [`src/auth.rs:22`](../src/auth.rs#L22) — `pub fn hash_token(token: &str) -> String`
- [`src/castcheck.rs:82`](../src/castcheck.rs#L82) — `pub fn evaluate(submitted: &[SubmittedActor], credits: &[CastMember]) -> CastCheckOutcome`
- [`src/castcheck.rs:215`](../src/castcheck.rs#L215) — `pub fn category_guard_violation(matched: &[MatchedActor], title_is_adult: bool) -> Option…`
@@ -379,3 +381,9 @@ _None._
- [`src/model.rs:191`](../src/model.rs#L191) — `Unknown`
- [`src/model.rs:232`](../src/model.rs#L232) — `pub fn from_stored(s: &str) -> Option<Self>`
### UR-019
**Locations:** 1
- [`src/api/upload.rs:245`](../src/api/upload.rs#L245) — `pub async fn post_token(`