From 76297e66bc6fbd16a3c0d0a875068b9ebc8446e7 Mon Sep 17 00:00:00 2001 From: Eli Mallon Date: Wed, 12 Aug 2026 19:54:56 -0700 Subject: [PATCH 1/3] metadata: let streamers opt out of generative AI training Adds distributionPolicy.allowAiTraining to the place.stream.metadata lexicons, mirroring the tri-state shape of Bluesky's user-intents proposal (allow / disallow / undeclared). The preference is opt-in: segment minting treats an undeclared value as "not allowed" and stamps an explicit denial into every manifest, so all minted segments carry the streamer's intent whether or not they ever touched the setting. - ManifestBuilder always embeds distributionPolicy.allowAiTraining (defaulting to false) and adds the standard cawg.training-mining assertion (cawg.ai_generative_training: allowed/notAllowed) so the declaration is legible to any C2PA-aware tooling, not just ours - extractDistributionPolicy + localdb.Segment + ToStreamplaceSegment carry the flag through validation into the minted place.stream.segment record - content metadata form grows an opt-in checkbox under Distribution; saving without touching it writes an explicit allowAiTraining: false (also fixes a stale-closure bug: handleSave was missing distributionPolicy in its useCallback deps, so distribution edits could save stale state) Co-Authored-By: Claude Fable 5 --- js/app/store/slices/contentMetadataSlice.ts | 14 ++- .../content-metadata-form.tsx | 27 ++++ ...lace-stream-metadata-distributionpolicy.md | 13 +- .../stream/metadata/distributionPolicy.json | 4 + pkg/localdb/segment.go | 6 +- pkg/media/distribution_policy_test.go | 77 ++++++++++++ pkg/media/manifest_builder.go | 53 ++++++-- pkg/media/manifest_builder_test.go | 117 ++++++++++++++++++ pkg/media/media.go | 17 ++- pkg/placestream/metadatadistributionpolicy.go | 2 + 10 files changed, 304 insertions(+), 26 deletions(-) create mode 100644 pkg/media/distribution_policy_test.go create mode 100644 pkg/media/manifest_builder_test.go diff --git a/js/app/store/slices/contentMetadataSlice.ts b/js/app/store/slices/contentMetadataSlice.ts index 91fbbe1f0..f0dbf54e9 100644 --- a/js/app/store/slices/contentMetadataSlice.ts +++ b/js/app/store/slices/contentMetadataSlice.ts @@ -14,7 +14,7 @@ export interface ContentMetadataSlice { // actions createContentMetadata: (params: { contentWarnings?: string[]; - distributionPolicy?: { deleteAfter?: number }; + distributionPolicy?: { deleteAfter?: number; allowAiTraining?: boolean }; contentRights?: { creator?: string; copyrightNotice?: string; @@ -27,7 +27,7 @@ export interface ContentMetadataSlice { rkey?: string; livestreamRef?: { uri: string; cid: string }; contentWarnings?: string[]; - distributionPolicy?: { deleteAfter?: number }; + distributionPolicy?: { deleteAfter?: number; allowAiTraining?: boolean }; contentRights?: { creator?: string; copyrightNotice?: string; @@ -80,7 +80,10 @@ export const createContentMetadataSlice: StateCreator< ...(contentWarnings.length > 0 && { contentWarnings: { warnings: contentWarnings }, }), - ...(distributionPolicy.deleteAfter && { distributionPolicy }), + ...((distributionPolicy.deleteAfter || + distributionPolicy.allowAiTraining !== undefined) && { + distributionPolicy, + }), ...(contentRights && Object.keys(contentRights).length > 0 && { contentRights, @@ -142,7 +145,10 @@ export const createContentMetadataSlice: StateCreator< ...(contentWarnings.length > 0 && { contentWarnings: { warnings: contentWarnings }, }), - ...(distributionPolicy.deleteAfter && { distributionPolicy }), + ...((distributionPolicy.deleteAfter || + distributionPolicy.allowAiTraining !== undefined) && { + distributionPolicy, + }), ...(contentRights && Object.keys(contentRights).length > 0 && { contentRights, diff --git a/js/components/src/components/content-metadata/content-metadata-form.tsx b/js/components/src/components/content-metadata/content-metadata-form.tsx index a21ec8156..f4d19a24e 100644 --- a/js/components/src/components/content-metadata/content-metadata-form.tsx +++ b/js/components/src/components/content-metadata/content-metadata-form.tsx @@ -198,14 +198,19 @@ export const ContentMetadataForm = forwardRef( ({ deleteAfter, allowedBroadcasters, + allowAiTraining, }: { deleteAfter?: string; allowedBroadcasters?: string; + allowAiTraining?: boolean; }) => { let newDistributionPolicy: place.stream.metadata.distributionPolicy.Main = { ...distributionPolicy, }; + if (typeof allowAiTraining === "boolean") { + newDistributionPolicy.allowAiTraining = allowAiTraining; + } if (typeof deleteAfter === "string") { let duration = parseInt(deleteAfter, 10); if (isNaN(duration)) { @@ -304,7 +309,10 @@ export const ContentMetadataForm = forwardRef( metadata.contentRights = filteredRights; } + // AI training is opt-in: absent an explicit choice, save the record + // with training disallowed. metadata.distributionPolicy = { + allowAiTraining: false, ...distributionPolicy, }; @@ -336,6 +344,7 @@ export const ContentMetadataForm = forwardRef( }, [ contentWarnings, contentRights, + distributionPolicy, selectedLicense, customLicenseText, hasMetadata, @@ -760,6 +769,24 @@ export const ContentMetadataForm = forwardRef( )} + + + handleDistributionPolicyChange({ + allowAiTraining: checked, + }) + } + label={ + "Allow your content to be used for generative AI training" + } + style={[{ fontSize: 12 }]} + /> + + Date: Wed, 12 Aug 2026 20:01:05 -0700 Subject: [PATCH 2/3] metadata: rename allowAiTraining -> allowGenAiTraining Be explicit that the flag governs generative AI only: Streamplace may still run non-generative machine learning (moderation, accessibility, speech models), and the lexicon description now says so. The CAWG assertion was already scoped to cawg.ai_generative_training. Co-Authored-By: Claude Fable 5 --- js/app/store/slices/contentMetadataSlice.ts | 8 ++--- .../content-metadata-form.tsx | 14 ++++----- ...lace-stream-metadata-distributionpolicy.md | 14 ++++----- .../stream/metadata/distributionPolicy.json | 4 +-- pkg/localdb/segment.go | 8 ++--- pkg/media/distribution_policy_test.go | 30 +++++++++---------- pkg/media/manifest_builder.go | 10 +++---- pkg/media/manifest_builder_test.go | 20 ++++++------- pkg/media/media.go | 4 +-- pkg/placestream/metadatadistributionpolicy.go | 4 +-- 10 files changed, 58 insertions(+), 58 deletions(-) diff --git a/js/app/store/slices/contentMetadataSlice.ts b/js/app/store/slices/contentMetadataSlice.ts index f0dbf54e9..297ca7594 100644 --- a/js/app/store/slices/contentMetadataSlice.ts +++ b/js/app/store/slices/contentMetadataSlice.ts @@ -14,7 +14,7 @@ export interface ContentMetadataSlice { // actions createContentMetadata: (params: { contentWarnings?: string[]; - distributionPolicy?: { deleteAfter?: number; allowAiTraining?: boolean }; + distributionPolicy?: { deleteAfter?: number; allowGenAiTraining?: boolean }; contentRights?: { creator?: string; copyrightNotice?: string; @@ -27,7 +27,7 @@ export interface ContentMetadataSlice { rkey?: string; livestreamRef?: { uri: string; cid: string }; contentWarnings?: string[]; - distributionPolicy?: { deleteAfter?: number; allowAiTraining?: boolean }; + distributionPolicy?: { deleteAfter?: number; allowGenAiTraining?: boolean }; contentRights?: { creator?: string; copyrightNotice?: string; @@ -81,7 +81,7 @@ export const createContentMetadataSlice: StateCreator< contentWarnings: { warnings: contentWarnings }, }), ...((distributionPolicy.deleteAfter || - distributionPolicy.allowAiTraining !== undefined) && { + distributionPolicy.allowGenAiTraining !== undefined) && { distributionPolicy, }), ...(contentRights && @@ -146,7 +146,7 @@ export const createContentMetadataSlice: StateCreator< contentWarnings: { warnings: contentWarnings }, }), ...((distributionPolicy.deleteAfter || - distributionPolicy.allowAiTraining !== undefined) && { + distributionPolicy.allowGenAiTraining !== undefined) && { distributionPolicy, }), ...(contentRights && diff --git a/js/components/src/components/content-metadata/content-metadata-form.tsx b/js/components/src/components/content-metadata/content-metadata-form.tsx index f4d19a24e..26a3e3dc1 100644 --- a/js/components/src/components/content-metadata/content-metadata-form.tsx +++ b/js/components/src/components/content-metadata/content-metadata-form.tsx @@ -198,18 +198,18 @@ export const ContentMetadataForm = forwardRef( ({ deleteAfter, allowedBroadcasters, - allowAiTraining, + allowGenAiTraining, }: { deleteAfter?: string; allowedBroadcasters?: string; - allowAiTraining?: boolean; + allowGenAiTraining?: boolean; }) => { let newDistributionPolicy: place.stream.metadata.distributionPolicy.Main = { ...distributionPolicy, }; - if (typeof allowAiTraining === "boolean") { - newDistributionPolicy.allowAiTraining = allowAiTraining; + if (typeof allowGenAiTraining === "boolean") { + newDistributionPolicy.allowGenAiTraining = allowGenAiTraining; } if (typeof deleteAfter === "string") { let duration = parseInt(deleteAfter, 10); @@ -312,7 +312,7 @@ export const ContentMetadataForm = forwardRef( // AI training is opt-in: absent an explicit choice, save the record // with training disallowed. metadata.distributionPolicy = { - allowAiTraining: false, + allowGenAiTraining: false, ...distributionPolicy, }; @@ -774,10 +774,10 @@ export const ContentMetadataForm = forwardRef( position="top" > handleDistributionPolicyChange({ - allowAiTraining: checked, + allowGenAiTraining: checked, }) } label={ diff --git a/js/docs/src/content/docs/lex-reference/metadata/place-stream-metadata-distributionpolicy.md b/js/docs/src/content/docs/lex-reference/metadata/place-stream-metadata-distributionpolicy.md index 908471d64..ed55ad6cf 100644 --- a/js/docs/src/content/docs/lex-reference/metadata/place-stream-metadata-distributionpolicy.md +++ b/js/docs/src/content/docs/lex-reference/metadata/place-stream-metadata-distributionpolicy.md @@ -17,11 +17,11 @@ Distribution and rebroadcast policy. **Properties:** -| Name | Type | Req'd | Description | Constraints | -| --------------------- | ----------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -| `deleteAfter` | `integer` | ❌ | Duration in seconds after which segments should be deleted. Each segment will expire N seconds after its creation time. -1 to allow indefinite archival. | | -| `allowedBroadcasters` | Array of `string` | ❌ | List of did:webs of the broadcasters you want to allow to distribute your content. "\*" allows anyone. Starting a line with a "!" bans that broadcaster. | | -| `allowAiTraining` | `boolean` | ❌ | Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment. | | +| Name | Type | Req'd | Description | Constraints | +| --------------------- | ----------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| `deleteAfter` | `integer` | ❌ | Duration in seconds after which segments should be deleted. Each segment will expire N seconds after its creation time. -1 to allow indefinite archival. | | +| `allowedBroadcasters` | Array of `string` | ❌ | List of did:webs of the broadcasters you want to allow to distribute your content. "\*" allows anyone. Starting a line with a "!" bans that broadcaster. | | +| `allowGenAiTraining` | `boolean` | ❌ | Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment. Covers generative AI only — non-generative machine learning (e.g. moderation, accessibility, speech models) is not governed by this flag. | | --- @@ -47,9 +47,9 @@ Distribution and rebroadcast policy. "type": "string" } }, - "allowAiTraining": { + "allowGenAiTraining": { "type": "boolean", - "description": "Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment." + "description": "Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment. Covers generative AI only — non-generative machine learning (e.g. moderation, accessibility, speech models) is not governed by this flag." } } } diff --git a/lexicons/place/stream/metadata/distributionPolicy.json b/lexicons/place/stream/metadata/distributionPolicy.json index eec3c1e47..9965e6715 100644 --- a/lexicons/place/stream/metadata/distributionPolicy.json +++ b/lexicons/place/stream/metadata/distributionPolicy.json @@ -17,9 +17,9 @@ "type": "string" } }, - "allowAiTraining": { + "allowGenAiTraining": { "type": "boolean", - "description": "Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment." + "description": "Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment. Covers generative AI only — non-generative machine learning (e.g. moderation, accessibility, speech models) is not governed by this flag." } } } diff --git a/pkg/localdb/segment.go b/pkg/localdb/segment.go index 2e137ae55..30c0aad9b 100644 --- a/pkg/localdb/segment.go +++ b/pkg/localdb/segment.go @@ -86,7 +86,7 @@ func (c ContentRights) Value() (driver.Value, error) { // DistributionPolicy represents distribution policy information type DistributionPolicy struct { DeleteAfterSeconds *int64 `json:"deleteAfterSeconds,omitempty"` - AllowAiTraining *bool `json:"allowAiTraining,omitempty"` + AllowGenAiTraining *bool `json:"allowGenAiTraining,omitempty"` } // Scan scan value into DistributionPolicy, implements sql.Scanner interface @@ -185,10 +185,10 @@ func (s *Segment) ToStreamplaceSegment() (*placestream.Segment, error) { } var distributionPolicy *placestream.MetadataDistributionPolicy - if s.DistributionPolicy != nil && (s.DistributionPolicy.DeleteAfterSeconds != nil || s.DistributionPolicy.AllowAiTraining != nil) { + if s.DistributionPolicy != nil && (s.DistributionPolicy.DeleteAfterSeconds != nil || s.DistributionPolicy.AllowGenAiTraining != nil) { distributionPolicy = &placestream.MetadataDistributionPolicy{ - DeleteAfter: s.DistributionPolicy.DeleteAfterSeconds, - AllowAiTraining: s.DistributionPolicy.AllowAiTraining, + DeleteAfter: s.DistributionPolicy.DeleteAfterSeconds, + AllowGenAiTraining: s.DistributionPolicy.AllowGenAiTraining, } } diff --git a/pkg/media/distribution_policy_test.go b/pkg/media/distribution_policy_test.go index f1efefc53..3d195c445 100644 --- a/pkg/media/distribution_policy_test.go +++ b/pkg/media/distribution_policy_test.go @@ -23,38 +23,38 @@ func manifestWithDistributionPolicy(policy map[string]any) *c2patypes.Manifest { } } -// extractDistributionPolicy carries allowAiTraining out of the manifest, alone +// extractDistributionPolicy carries allowGenAiTraining out of the manifest, alone // or alongside deleteAfter, and stays nil when neither is declared. -func TestExtractDistributionPolicyAllowAiTraining(t *testing.T) { +func TestExtractDistributionPolicyAllowGenAiTraining(t *testing.T) { now := aqtime.FromTime(time.Now()) policy := extractDistributionPolicy(manifestWithDistributionPolicy(map[string]any{ - "allowAiTraining": false, + "allowGenAiTraining": false, }), now) require.NotNil(t, policy) - require.NotNil(t, policy.AllowAiTraining) - require.False(t, *policy.AllowAiTraining) + require.NotNil(t, policy.AllowGenAiTraining) + require.False(t, *policy.AllowGenAiTraining) require.Nil(t, policy.DeleteAfterSeconds) policy = extractDistributionPolicy(manifestWithDistributionPolicy(map[string]any{ - "allowAiTraining": true, - "deleteAfter": 300, + "allowGenAiTraining": true, + "deleteAfter": 300, }), now) require.NotNil(t, policy) - require.NotNil(t, policy.AllowAiTraining) - require.True(t, *policy.AllowAiTraining) + require.NotNil(t, policy.AllowGenAiTraining) + require.True(t, *policy.AllowGenAiTraining) require.NotNil(t, policy.DeleteAfterSeconds) require.Equal(t, int64(300), *policy.DeleteAfterSeconds) policy = extractDistributionPolicy(manifestWithDistributionPolicy(map[string]any{ "allowedBroadcasters": []string{"*"}, }), now) - require.Nil(t, policy, "a policy with neither deleteAfter nor allowAiTraining stays nil") + require.Nil(t, policy, "a policy with neither deleteAfter nor allowGenAiTraining stays nil") } -// The minted place.stream.segment record carries allowAiTraining even when +// The minted place.stream.segment record carries allowGenAiTraining even when // deleteAfter is unset. -func TestSegmentRecordCarriesAllowAiTraining(t *testing.T) { +func TestSegmentRecordCarriesAllowGenAiTraining(t *testing.T) { notAllowed := false seg := &localdb.Segment{ ID: "test-segment", @@ -65,13 +65,13 @@ func TestSegmentRecordCarriesAllowAiTraining(t *testing.T) { Audio: []*localdb.SegmentMediadataAudio{{Rate: 48000, Channels: 2}}, }, DistributionPolicy: &localdb.DistributionPolicy{ - AllowAiTraining: ¬Allowed, + AllowGenAiTraining: ¬Allowed, }, } record, err := seg.ToStreamplaceSegment() require.NoError(t, err) require.NotNil(t, record.DistributionPolicy) - require.NotNil(t, record.DistributionPolicy.AllowAiTraining) - require.False(t, *record.DistributionPolicy.AllowAiTraining) + require.NotNil(t, record.DistributionPolicy.AllowGenAiTraining) + require.False(t, *record.DistributionPolicy.AllowGenAiTraining) require.Nil(t, record.DistributionPolicy.DeleteAfter) } diff --git a/pkg/media/manifest_builder.go b/pkg/media/manifest_builder.go index 266626591..1463d9b72 100644 --- a/pkg/media/manifest_builder.go +++ b/pkg/media/manifest_builder.go @@ -159,9 +159,9 @@ func (mb *ManifestBuilder) BuildManifest(ctx context.Context, streamerName strin if streamplaceMetadata.DistributionPolicy == nil { streamplaceMetadata.DistributionPolicy = &placestream.MetadataDistributionPolicy{} } - if streamplaceMetadata.DistributionPolicy.AllowAiTraining == nil { + if streamplaceMetadata.DistributionPolicy.AllowGenAiTraining == nil { notAllowed := false - streamplaceMetadata.DistributionPolicy.AllowAiTraining = ¬Allowed + streamplaceMetadata.DistributionPolicy.AllowGenAiTraining = ¬Allowed } metadataObj, err := toObj(streamplaceMetadata) if err != nil { @@ -172,7 +172,7 @@ func (mb *ManifestBuilder) BuildManifest(ctx context.Context, streamerName strin "label": "place.stream.metadata.configuration", "data": metadataObj, }, - trainingMiningAssertion(*streamplaceMetadata.DistributionPolicy.AllowAiTraining), + trainingMiningAssertion(*streamplaceMetadata.DistributionPolicy.AllowGenAiTraining), ) // Update the manifest title with the retrieved livestream title @@ -199,9 +199,9 @@ func (mb *ManifestBuilder) BuildManifest(ctx context.Context, streamerName strin // trainingMiningAssertion builds the CAWG training and data mining assertion // (https://cawg.io/training-and-data-mining/1.1/), the standard machine-readable // way to declare whether content may be used to train generative AI models. -func trainingMiningAssertion(allowAiTraining bool) obj { +func trainingMiningAssertion(allowGenAiTraining bool) obj { use := "notAllowed" - if allowAiTraining { + if allowGenAiTraining { use = "allowed" } return obj{ diff --git a/pkg/media/manifest_builder_test.go b/pkg/media/manifest_builder_test.go index b423eb14e..57d8f8035 100644 --- a/pkg/media/manifest_builder_test.go +++ b/pkg/media/manifest_builder_test.go @@ -57,7 +57,7 @@ func buildManifestAssertions(t *testing.T, metadata *model.MetadataConfiguration return out } -func aiTrainingUse(t *testing.T, assertions map[string]map[string]any) string { +func genAiTrainingUse(t *testing.T, assertions map[string]map[string]any) string { data, ok := assertions["cawg.training-mining"] require.True(t, ok, "manifest must carry the cawg.training-mining assertion") entries, ok := data["entries"].(map[string]any) @@ -69,20 +69,20 @@ func aiTrainingUse(t *testing.T, assertions map[string]map[string]any) string { return use } -func allowAiTrainingField(t *testing.T, assertions map[string]map[string]any) any { +func allowGenAiTrainingField(t *testing.T, assertions map[string]map[string]any) any { data, ok := assertions["place.stream.metadata.configuration"] require.True(t, ok, "manifest must carry the metadata configuration assertion") policy, ok := data["distributionPolicy"].(map[string]any) require.True(t, ok, "metadata configuration must carry a distributionPolicy") - return policy["allowAiTraining"] + return policy["allowGenAiTraining"] } // A streamer with no metadata configuration at all still gets an explicit // "AI training not allowed" stamped into every minted segment. func TestBuildManifestAiTrainingDefaultDeny(t *testing.T) { assertions := buildManifestAssertions(t, nil) - require.Equal(t, "notAllowed", aiTrainingUse(t, assertions)) - require.Equal(t, false, allowAiTrainingField(t, assertions)) + require.Equal(t, "notAllowed", genAiTrainingUse(t, assertions)) + require.Equal(t, false, allowGenAiTrainingField(t, assertions)) } // A metadata configuration that never mentions AI training also defaults to @@ -95,8 +95,8 @@ func TestBuildManifestAiTrainingUndeclaredDeny(t *testing.T) { }, }) assertions := buildManifestAssertions(t, row) - require.Equal(t, "notAllowed", aiTrainingUse(t, assertions)) - require.Equal(t, false, allowAiTrainingField(t, assertions)) + require.Equal(t, "notAllowed", genAiTrainingUse(t, assertions)) + require.Equal(t, false, allowGenAiTrainingField(t, assertions)) // the rest of the distribution policy survives the stamping policy := assertions["place.stream.metadata.configuration"]["distributionPolicy"].(map[string]any) @@ -108,10 +108,10 @@ func TestBuildManifestAiTrainingExplicitAllow(t *testing.T) { allow := true row := metadataConfigurationRow(t, &placestream.MetadataConfiguration{ DistributionPolicy: &placestream.MetadataDistributionPolicy{ - AllowAiTraining: &allow, + AllowGenAiTraining: &allow, }, }) assertions := buildManifestAssertions(t, row) - require.Equal(t, "allowed", aiTrainingUse(t, assertions)) - require.Equal(t, true, allowAiTrainingField(t, assertions)) + require.Equal(t, "allowed", genAiTrainingUse(t, assertions)) + require.Equal(t, true, allowGenAiTrainingField(t, assertions)) } diff --git a/pkg/media/media.go b/pkg/media/media.go index 8893f0b10..81096c8af 100644 --- a/pkg/media/media.go +++ b/pkg/media/media.go @@ -428,7 +428,7 @@ func extractDistributionPolicy(mani *c2patypes.Manifest, segmentStart aqtime.AQT } policy := &localdb.DistributionPolicy{ - AllowAiTraining: metadataConfig.DistributionPolicy.AllowAiTraining, + AllowGenAiTraining: metadataConfig.DistributionPolicy.AllowGenAiTraining, } if metadataConfig.DistributionPolicy.DeleteAfter != nil { @@ -437,7 +437,7 @@ func extractDistributionPolicy(mani *c2patypes.Manifest, segmentStart aqtime.AQT policy.DeleteAfterSeconds = &deleteAfterSeconds } - if policy.DeleteAfterSeconds == nil && policy.AllowAiTraining == nil { + if policy.DeleteAfterSeconds == nil && policy.AllowGenAiTraining == nil { return nil } diff --git a/pkg/placestream/metadatadistributionpolicy.go b/pkg/placestream/metadatadistributionpolicy.go index 4598ab67a..6449793fd 100644 --- a/pkg/placestream/metadatadistributionpolicy.go +++ b/pkg/placestream/metadatadistributionpolicy.go @@ -19,8 +19,8 @@ func init() { // Distribution and rebroadcast policy. type MetadataDistributionPolicy struct { LexiconTypeID string `json:"$type,omitempty"` - // allowAiTraining: Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment. - AllowAiTraining *bool `json:"allowAiTraining,omitempty"` + // allowGenAiTraining: Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment. Covers generative AI only — non-generative machine learning (e.g. moderation, accessibility, speech models) is not governed by this flag. + AllowGenAiTraining *bool `json:"allowGenAiTraining,omitempty"` // allowedBroadcasters: List of did:webs of the broadcasters you want to allow to distribute your content. "*" allows anyone. Starting a line with a "!" bans that broadcaster. AllowedBroadcasters []string `json:"allowedBroadcasters,omitempty"` // deleteAfter: Duration in seconds after which segments should be deleted. Each segment will expire N seconds after its creation time. -1 to allow indefinite archival. -- 2.51.2 From 92220faea4018ad912600e0ebf4fa49662e7fe9c Mon Sep 17 00:00:00 2001 From: Eli Mallon Date: Wed, 12 Aug 2026 21:10:27 -0700 Subject: [PATCH 3/3] ci: retrigger flaky builds linux binaries hit pnpm socket hang-ups; the GitLab test job tripped the known-flaky TestConcatDemuxBin leak check (passes locally, twice). Co-Authored-By: Claude Fable 5 -- 2.51.2