commit fecb3049a4b5413f42abe58d5d9065b495c0dcfa
parent 5633c4c5159af3c2db76418e0e2b29ba67e5af36
Author: William Casarin <jb55@jb55.com>
Date: Thu, 12 Feb 2026 13:50:12 -0800
Merge UGC compliance plan docs #1287
Diffstat:
5 files changed, 326 insertions(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
@@ -24,3 +24,5 @@ scripts/macos_build_secrets.sh
.lsp
.idea
local.properties
+keystore.properties
+*.keystore
diff --git a/Makefile b/Makefile
@@ -29,6 +29,14 @@ android: jni
adb shell am start -n com.damus.notedeck/.MainActivity
adb logcat -v color -s GameActivity -s RustStdoutStderr -s threaded_app | tee logcat.txt
+release-apk: jni
+ cd $(ANDROID_DIR) && ./gradlew assembleRelease
+ @echo "Signed APK: $(ANDROID_DIR)/app/build/outputs/apk/release/app-release.apk"
+
+release-aab: jni
+ cd $(ANDROID_DIR) && ./gradlew bundleRelease
+ @echo "Signed AAB: $(ANDROID_DIR)/app/build/outputs/bundle/release/app-release.aab"
+
android-tracy: fake
cargo ndk --target arm64-v8a -o $(ANDROID_DIR)/app/src/main/jniLibs/ build --profile release --features tracy
cd $(ANDROID_DIR) && ./gradlew installDebug
diff --git a/crates/notedeck_chrome/android/app/build.gradle b/crates/notedeck_chrome/android/app/build.gradle
@@ -2,6 +2,12 @@ plugins {
id "com.android.application"
}
+def keystorePropertiesFile = rootProject.file("keystore.properties")
+def keystoreProperties = new Properties()
+if (keystorePropertiesFile.exists()) {
+ keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+}
+
android {
namespace "com.damus.notedeck"
compileSdk 31
@@ -13,8 +19,23 @@ android {
versionName "1"
}
+ signingConfigs {
+ release {
+ if (keystorePropertiesFile.exists()) {
+ storeFile file(keystoreProperties['storeFile'])
+ storePassword keystoreProperties['storePassword']
+ keyAlias keystoreProperties['keyAlias']
+ keyPassword keystoreProperties['keyPassword']
+ }
+ }
+ }
+
buildTypes {
- debug
+ debug {}
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ }
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
diff --git a/docs/EULA.md b/docs/EULA.md
@@ -0,0 +1,114 @@
+**End User License Agreement**
+
+**Last Updated: 2026-02-04**
+
+**Introduction**
+
+This End User License Agreement ("EULA") is a legal agreement between you and Damus Nostr Inc. ("we", "us", or "our") for the use of the Damus mobile application ("Application"). By installing, accessing, or using our Application, you agree to be bound by the terms and conditions of this EULA. If you do not agree to these terms, do not install or use the Application.
+
+**Age Requirement**
+
+You must be at least 17 years of age to use this Application. By using the Application, you represent and warrant that you are at least 17 years old. If you are under 17, you may not use this Application.
+
+**Nature of the Application**
+
+Damus is a client application for the Nostr protocol, a decentralized social networking protocol. Content on Nostr is published to independent relay servers, most of which are operated by third parties.
+
+**Important:** Due to the decentralized nature of the Nostr protocol:
+- Content published to Nostr relays may be replicated across many servers and cannot be fully deleted or removed
+- We do not control or moderate most Nostr relay servers
+- Content you publish may be permanently stored on relays outside our control
+- Blocking or muting users in the Application only affects your local view; it does not prevent others from seeing your content on the Nostr network
+
+**Prohibited Content and Conduct**
+
+You agree not to use our Application to create, upload, post, send, or store any content that:
+
+- Is illegal under applicable law, including but not limited to content that violates laws regarding child exploitation, terrorism, or fraud
+- Depicts, promotes, or facilitates child sexual abuse or exploitation
+- Is defamatory, libelous, or threatening
+- Is pornographic, obscene, or sexually explicit without appropriate content warnings
+- Is discriminatory or promotes hatred against individuals or groups based on race, ethnicity, religion, gender, sexual orientation, disability, or national origin
+- Is harmful to minors
+- Is intended to harass, bully, or intimidate others
+- Impersonates any person or entity, or falsely represents your affiliation with a person or entity
+- Contains malware, viruses, or any code designed to harm or compromise systems
+- Is spam, including unsolicited commercial messages or repeated unwanted content
+- Infringes on the intellectual property rights of others
+
+**You also agree not to engage in any conduct that:**
+
+- Harasses, bullies, or threatens others
+- Impersonates others or misrepresents your identity
+- Promotes or incites violence against any individual or group
+- Attempts to circumvent any security features of the Application
+- Interferes with or disrupts the Application or connected services
+- Collects or harvests user information without consent
+
+**Reporting and Blocking**
+
+The Application provides tools to report objectionable content and block users:
+
+- **Reporting:** You may report content or users that violate this EULA. Reports are published as Nostr events (NIP-56) and may be used by relay operators and other clients for moderation purposes.
+- **Blocking:** You may block users to hide their content from your view. Blocking is local to your device and does not prevent blocked users from viewing your public content on the Nostr network.
+- **Muting:** You may mute users, hashtags, or threads to reduce unwanted content in your feeds.
+
+We encourage you to use these tools to manage your experience on the Application.
+
+**User Responsibility**
+
+You are solely responsible for:
+- All content you create, publish, or share through the Application
+- Ensuring your content complies with this EULA and applicable laws
+- Any consequences resulting from your content or conduct
+- Maintaining the security of your private keys and account credentials
+
+**Consequences of Violation**
+
+Violation of this EULA may result in:
+- Termination or suspension of your access to the Application
+- Removal of locally stored data associated with your account
+- Reporting of illegal activity to appropriate law enforcement authorities
+
+Due to the decentralized nature of Nostr, we cannot remove content you have published to third-party relay servers, but we reserve the right to restrict your use of our Application.
+
+**Disclaimer of Warranties**
+
+THE APPLICATION IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
+
+WE DO NOT WARRANT THAT:
+- The Application will be uninterrupted, secure, or error-free
+- Content from other users will be accurate, lawful, or appropriate
+- The Application will meet your specific requirements
+- Any defects in the Application will be corrected
+
+**Limitation of Liability**
+
+TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL DAMUS NOSTR INC., ITS DIRECTORS, EMPLOYEES, PARTNERS, AGENTS, SUPPLIERS, OR AFFILIATES BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES, INCLUDING WITHOUT LIMITATION LOSS OF PROFITS, DATA, USE, GOODWILL, OR OTHER INTANGIBLE LOSSES, RESULTING FROM:
+
+- Your access to, use of, or inability to access or use the Application
+- Any conduct or content of any third party on the Application or Nostr network
+- Any content obtained from the Application or Nostr network
+- Unauthorized access, use, or alteration of your transmissions or content
+
+**Indemnification**
+
+You agree to indemnify, defend, and hold harmless Damus Nostr Inc. and its officers, directors, employees, and agents from and against any claims, liabilities, damages, losses, and expenses arising out of or in any way connected with your access to or use of the Application or your violation of this EULA.
+
+**Changes to EULA**
+
+We reserve the right to update or modify this EULA at any time. We will notify you of material changes by updating the "Last Updated" date and, where feasible, through the Application. Your continued use of the Application following any changes constitutes your acceptance of the revised EULA.
+
+**Severability**
+
+If any provision of this EULA is held to be unenforceable or invalid, such provision will be changed and interpreted to accomplish the objectives of such provision to the greatest extent possible under applicable law, and the remaining provisions will continue in full force and effect.
+
+**Contact Information**
+
+If you have any questions about this EULA, or to report violations, please contact us at:
+
+Email: support@damus.io
+
+**Acceptance of Terms**
+
+By installing, accessing, or using our Application, you acknowledge that you have read, understood, and agree to be bound by this EULA. If you do not agree to this EULA, you may not use our Application.
diff --git a/docs/UGC_COMPLIANCE_PLAN.md b/docs/UGC_COMPLIANCE_PLAN.md
@@ -0,0 +1,180 @@
+# Google Play Store UGC Compliance Plan for notedeck_columns
+
+## Overview
+
+Implement User Generated Content (UGC) compliance features required by Google Play Store for the notedeck_columns Nostr client app.
+
+## Current State
+
+- **Has**: Client-side muting (pubkeys, hashtags, threads), blurhash media obfuscation for non-followed users
+- **Missing**: TOS acceptance, user blocking, content reporting, age verification
+
+## Requirements from Google Play
+
+1. TOS acceptance before UGC creation
+2. Define objectionable content in TOS
+3. In-app reporting system for users and content
+4. User blocking functionality (required for DM/mention features)
+
+---
+
+## Implementation Plan
+
+### Phase 1: Data Structures & Storage
+
+**1.1 Create compliance module** - `crates/notedeck/src/compliance.rs`
+```rust
+pub struct ComplianceData {
+ pub tos_accepted: bool,
+ pub tos_accepted_at: Option<u64>,
+ pub tos_version: String, // e.g., "1.0"
+ pub age_verified: bool,
+}
+```
+
+**1.2 Add blocked users to Muted** - `crates/notedeck/src/muted.rs`
+- Add `blocked_pubkeys: BTreeSet<[u8; 32]>` field
+- Add `is_blocked(pk)` method
+- Blocked = completely hidden (stronger than muted)
+
+**1.3 Extend Settings** - `crates/notedeck/src/persist/settings_handler.rs`
+- Add `tos_accepted`, `tos_accepted_at`, `tos_version`, `age_verified` fields
+- Add `blocked_pubkeys` (or store per-account)
+
+### Phase 2: TOS & Age Verification Screen
+
+**2.1 Create TOS route** - `crates/notedeck_columns/src/route.rs`
+- Add `Route::TosAcceptance`
+
+**2.2 Create TOS UI** - `crates/notedeck_columns/src/ui/side_panel/tos.rs` (new)
+- Full-screen modal with:
+ - Scrollable TOS text (embedded, you provide content)
+ - Checkbox: "I confirm I am 17 years or older"
+ - Checkbox: "I agree to the Terms of Service and Community Guidelines"
+ - "Accept and Continue" button (disabled until both checked)
+
+**2.3 Gate UGC creation**
+- Modify `crates/notedeck_columns/src/ui/note/post.rs` - check TOS before post
+- Modify note reply flow - check TOS before reply
+- Modify `crates/notedeck_messages/` - check TOS before DM
+
+**2.4 Trigger on first launch**
+- In app startup, if `!settings.tos_accepted`, show TOS screen before main UI
+
+### Phase 3: Block User Feature
+
+**3.1 Add block to context menus**
+- `crates/notedeck_ui/src/note/context.rs` - add `BlockAuthor` option
+- `crates/notedeck_ui/src/profile/context.rs` - add `Block` option
+
+**3.2 Block confirmation dialog** - `crates/notedeck_columns/src/ui/side_panel/block.rs` (new)
+- "Block @username?"
+- "You won't see their posts, replies, or messages"
+- Block / Cancel buttons
+
+**3.3 Filter blocked content**
+- Modify timeline rendering to skip blocked pubkeys
+- Modify DM conversation list to hide blocked users
+- Modify notification filtering
+
+**3.4 Blocked users management**
+- Add to Settings UI: "Blocked Users" section with list and unblock option
+
+### Phase 4: Report Feature (NIP-56)
+
+**4.1 Create report event builder** - `crates/enostr/src/report.rs` (new)
+```rust
+// NIP-56 Report Event (kind 1984)
+pub fn create_report_note(note_id, author_pk, reason) -> NostrEvent
+pub fn create_report_profile(pubkey, reason) -> NostrEvent
+```
+
+Reasons (NIP-56): `nudity`, `malware`, `profanity`, `illegal`, `spam`, `impersonation`, `other`
+
+**4.2 Add report to context menus**
+- Note context menu: "Report Note"
+- Profile context menu: "Report User"
+
+**4.3 Report dialog UI** - `crates/notedeck_columns/src/ui/side_panel/report.rs` (new)
+- Select reason (dropdown/radio)
+- Optional description text
+- Submit / Cancel buttons
+- On submit: sign and publish NIP-56 event to relays
+
+### Phase 5: Settings Integration
+
+**5.1 Add "Content & Safety" section** to `crates/notedeck_columns/src/ui/settings.rs`
+- Blocked Users (list with unblock)
+- Muted Users (existing, but surface here too)
+- View Terms of Service
+- Content filtering toggle (hide sensitive media by default)
+
+---
+
+## Key Files to Modify
+
+| File | Changes |
+|------|---------|
+| `crates/notedeck/src/muted.rs` | Add `blocked_pubkeys`, `is_blocked()` |
+| `crates/notedeck/src/persist/settings_handler.rs` | Add TOS/compliance fields |
+| `crates/notedeck_columns/src/route.rs` | Add TOS, Report, BlockConfirm routes |
+| `crates/notedeck_ui/src/note/context.rs` | Add Block/Report menu options |
+| `crates/notedeck_ui/src/profile/context.rs` | Add Block/Report menu options |
+| `crates/notedeck_columns/src/ui/settings.rs` | Add Content & Safety section |
+| `crates/notedeck_columns/src/ui/note/post.rs` | Gate posting behind TOS |
+
+## New Files to Create
+
+| File | Purpose |
+|------|---------|
+| `crates/notedeck/src/compliance.rs` | ComplianceData struct |
+| `crates/notedeck_columns/src/ui/side_panel/tos.rs` | TOS acceptance screen |
+| `crates/notedeck_columns/src/ui/side_panel/block.rs` | Block confirmation dialog |
+| `crates/notedeck_columns/src/ui/side_panel/report.rs` | Report dialog |
+| `crates/enostr/src/report.rs` | NIP-56 report event creation |
+
+---
+
+## TOS Content Requirements
+
+The embedded TOS must define prohibited content (per Google Play):
+- Illegal content
+- Child sexual abuse material
+- Harassment and bullying
+- Hate speech and discrimination
+- Impersonation
+- Malware, phishing, spam
+- Sexually explicit content (note: Nostr is decentralized, explain user's responsibility)
+
+Include:
+- Age requirement (17+)
+- How to report content
+- How to block users
+- Disclaimer: decentralized protocol means content cannot be deleted from all relays
+
+**You will need to provide the actual TOS text.**
+
+---
+
+## Verification
+
+After implementation:
+1. Fresh install → TOS screen appears before main UI
+2. Cannot post/reply/DM until TOS accepted
+3. Block user from note → user's content disappears from timelines
+4. Block user from profile → same result
+5. Report note → NIP-56 event published to relays (check with relay or other client)
+6. Report profile → NIP-56 event published
+7. Blocked users visible in Settings, can unblock
+8. App restart → blocked users remain blocked, TOS remains accepted
+
+---
+
+## Out of Scope (Nostr Protocol Limitations)
+
+- Cannot delete content from relays (decentralized)
+- Cannot prevent blocked users from seeing your content
+- Cannot implement server-side moderation
+- Reports are informational (relays/clients may or may not act on them)
+
+These limitations should be disclosed in the TOS.