Skip to content

add go, python, react native, flutter libraries#5

Merged
alltheseas merged 10 commits intomainfrom
feat/multi-platform-sdks
Jan 16, 2026
Merged

add go, python, react native, flutter libraries#5
alltheseas merged 10 commits intomainfrom
feat/multi-platform-sdks

Conversation

@alltheseas
Copy link
Owner

@alltheseas alltheseas commented Jan 16, 2026

PR #5 adds three new SDKs:

│     SDK      │                                  Features                                  │
├──────────────┼────────────────────────────────────────────────────────────────────────────┤
│ Go           │ Panic recovery hook, NIP-17 gift wrap, gzip compression                    │
├──────────────┼────────────────────────────────────────────────────────────────────────────┤
│ Python       │ Exception hook, sys.excepthook integration, NIP-17 support                 │
├──────────────┼────────────────────────────────────────────────────────────────────────────┤
│ React Native │ ErrorBoundary component, native Alert consent dialog, global error handler │
└──────────────┴────────────────────────────────────────────────────────────────────────────┘

Plus fixes:

  • Go: Updated golang.org/x/crypto to v0.47.0 (CVE fixes)
  • Python: Added setuptools package discovery
  • React Native: Added NIP-01 rumor id computation, created tsconfig.build.json

Total: +1,767 lines across 15 files

All three SDKs follow the same pattern:

  1. init(developerPubkey) to configure
  2. Automatic crash capture via language-specific hooks
  3. User consent before sending
  4. NIP-17 encrypted delivery with 30-day expiration

Summary

  • Fix nip19.Decode usage (was using non-existent nostr.DecodeBech32)
  • Fix rumor ID computation per NIP-01 spec using proper SHA256 serialization
  • Update go.mod with resolved dependencies

Test plan

  • go build ./... compiles successfully
  • Manual test sending crash report via Go SDK

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added Go, Python, and React Native SDKs for crash reporting with automatic redaction, optional confirmation, and an Error Boundary for React Native.
    • Default relay configuration and platform test-status indicators added.
  • Documentation

    • New SDK READMEs; Size Limits & Compression guide with compression format, envelope behavior, truncation rules, and decompression details.
    • Expanded NIP compliance notes and required behaviors.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 16, 2026

Warning

Rate limit exceeded

@alltheseas has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 13 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 7951ebc and 57c4cb9.

📒 Files selected for processing (15)
  • AGENTS.md
  • README.md
  • dart/CHANGELOG.md
  • dart/README.md
  • dart/lib/bugstr.dart
  • dart/lib/src/bugstr_client.dart
  • dart/lib/src/bugstr_crash_handler.dart
  • dart/lib/src/bugstr_crash_report_cache.dart
  • dart/lib/src/bugstr_report_assembler.dart
  • dart/lib/src/config.dart
  • dart/lib/src/nip17_payload_builder.dart
  • dart/lib/src/payload.dart
  • dart/pubspec.yaml
  • go/bugstr.go
  • react-native/src/index.ts
📝 Walkthrough

Walkthrough

Adds new Go, Python, and React Native SDKs and docs implementing NIP-17/Nostr crash reporting, plus README updates describing default relays, size limits, compression envelope, redaction, confirmation hooks, and packaging/config files for each SDK.

Changes

Cohort / File(s) Summary
Main README
README.md
Adds platforms table entries (Go, Python, React Native), Default Relays, Size Limits & Compression (gzip + versioned envelope behavior, thresholds), compression efficiency examples, stack-trace truncation, and expanded NIP compliance notes.
Go SDK
go/README.md, go/bugstr.go, go/go.mod
New Go SDK and docs: public Config/Payload/Summary/CompressedEnvelope types; Init, Recover, RecoverAndContinue, CaptureException/CaptureMessage APIs; redaction, stack capture/truncation, optional gzip+envelope compression, NIP-17/NIP-44 transport (construct rumor -> seal -> gift), default relays/redactions, and module dependencies.
Python SDK
python/.gitignore, python/README.md, python/bugstr/__init__.py, python/pyproject.toml
New Python SDK and packaging: init, capture_exception, capture_message, install_exception_hook APIs; Config/Payload dataclasses; payload building, redaction, optional compression/envelope, async publish via nostr-sdk, pyproject metadata, and README with framework examples.
React Native SDK
react-native/.gitignore, react-native/README.md, react-native/package.json, react-native/src/index.ts, react-native/tsconfig.json, react-native/tsconfig.build.json
New TypeScript React Native SDK and docs: BugstrConfig/BugstrPayload types; init, captureException, captureMessage, ErrorBoundary; confirmation flow (native or callback), beforeSend hook, redaction, maybeCompress envelope, Nostr transport (rumor -> seal -> wrap -> publish), package manifest and TS build configs.
Misc config / packaging
react-native/package.json, react-native/tsconfig*.json, python/.gitignore
New publish and build configuration files (package.json, tsconfig, tsconfig.build) and ignore files for the new SDK packages.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant SDK as Bugstr SDK
    participant Hook as Hook (before_send/confirm_send)
    participant Relay as Nostr Relay
    participant Dev as Developer

    App->>SDK: error occurs / captureException(...)
    SDK->>SDK: buildPayload() — message, stack, ts, env, release
    SDK->>SDK: redact(payload)
    SDK->>SDK: maybeCompress(payload) -> optional gzip+envelope

    alt beforeSend hook present
        SDK->>Hook: before_send(payload)
        Hook-->>SDK: modified payload / cancel
    end

    alt confirmation required
        SDK->>Hook: confirm_send(summary)
        Hook-->>SDK: true / false
        alt user declines
            SDK-->>App: abort send
        end
    end

    SDK->>SDK: construct NIP-14 rumor -> seal (nip44 / kind-13) -> wrap (kind-1059)
    SDK->>Relay: publish wrapped event(s)
    Relay-->>Dev: deliver encrypted message
    Dev->>Dev: decrypt & inspect report
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Poem

🐰 I hopped through stacks and traces wide,

I wrapped your errors snug inside,
Encrypted hops across the air,
Go, Python, React — I sent them there,
A tiny rabbit’s careful pride 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'add go, python, react native, flutter libraries' accurately describes the main changes: introducing three new SDK implementations (Go, Python, React Native) with comprehensive documentation and code.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@alltheseas alltheseas force-pushed the feat/multi-platform-sdks branch from a50146d to f494df5 Compare January 16, 2026 08:06
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@go/go.mod`:
- Line 22: The go.mod currently pins the indirect dependency golang.org/x/crypto
at v0.28.0 which has multiple critical CVEs; determine the latest patched
release that addresses GO-2024-3321, GO-2025-3487, GO-2025-4116, GO-2025-4134,
GO-2025-4135, GHSA-f6x5-jh6r-wrfv, GHSA-hcg3-q754-cr77, GHSA-j5w8-q4qc-rx2x, and
GHSA-v778-237x-gjrc, then update the module to that version by updating the
golang.org/x/crypto dependency in the Go module (use the Go tooling to set
golang.org/x/crypto@<patched-version>) and run module cleanup (go mod tidy) so
the go.mod/go.sum reflect the fixed version.

In `@python/pyproject.toml`:
- Around line 1-35: The pyproject.toml is missing setuptools package discovery
configuration for the project "bugstr"; add explicit setuptools package
discovery by adding a [tool.setuptools.packages.find] table and include pattern
(e.g. include = ["bugstr*"]) or set packages = ["find:"] under [tool.setuptools]
so setuptools knows to locate the bugstr package (if you use a src/ layout also
add where = ["src"] in the find table).

In `@react-native/src/index.ts`:
- Around line 140-160: unsignedKind14 is missing the NIP-01 computed id (SHA-256
of [0, pubkey, created_at, kind, tags, content]), so compute the rumor id and
add it to unsignedKind14 before encrypting/sealing; use the same fields used to
build the event (pubkey from getPublicKey(senderPrivkey), created_at from
randomPastTimestamp(), kind = 14, tags, content = plaintext), compute the hex
SHA-256 digest and set unsignedKind14.id, then continue to derive
conversationKey via nip44.getConversationKey, encrypt with nip44.encrypt and
pass the finalized event to finalizeEvent as before so the recipient can decrypt
and verify the rumor structure per NIP-01.

In `@react-native/tsconfig.json`:
- Around line 1-16: The build is failing because react-native-builder-bob is
pointed at "tsconfig.build.json" but only "tsconfig.json" exists; either add a
new tsconfig.build.json that extends tsconfig.json and excludes tests/mocks
(e.g., extends "./tsconfig.json" and exclude
["node_modules","lib","**/__tests__","**/__mocks__"]) or update package.json's
builder config to reference "tsconfig.json" instead; locate the package.json
builder entry referencing "project": "tsconfig.build.json" and choose one of
these fixes, ensuring the final project file name and exclusion rules match the
intended build output.
🧹 Nitpick comments (8)
react-native/package.json (2)

46-53: @types/react-native may be unnecessary with React Native 0.73+.

React Native 0.73 ships with built-in TypeScript types, so @types/react-native is typically no longer needed and may cause type conflicts.

Consider removing `@types/react-native`
   "devDependencies": {
     "@types/react": "^18.2.0",
-    "@types/react-native": "^0.72.0",
     "react": "18.2.0",
     "react-native": "0.73.0",
     "react-native-builder-bob": "^0.23.0",
     "typescript": "^5.3.0"
   },

54-57: Consider specifying minimum peer dependency versions.

Using "*" for peer dependencies is very permissive and may lead to compatibility issues with older versions. Consider specifying minimum supported versions.

Specify minimum peer dependency versions
   "peerDependencies": {
-    "react": "*",
-    "react-native": "*"
+    "react": ">=17.0.0",
+    "react-native": ">=0.68.0"
   },
go/bugstr.go (3)

201-213: Unsafe type assertion may panic.

At line 210, data.(string) will panic if nip19.Decode returns a non-string type for npub (though this should be the expected type). Adding a type check makes this more defensive.

Suggested safer type assertion
 func decodePubkey(pubkey string) string {
 	if pubkey == "" {
 		return ""
 	}
 	if strings.HasPrefix(pubkey, "npub") {
 		prefix, data, err := nip19.Decode(pubkey)
 		if err != nil || prefix != "npub" {
 			return ""
 		}
-		return data.(string)
+		hex, ok := data.(string)
+		if !ok {
+			return ""
+		}
+		return hex
 	}
 	return pubkey
 }

269-282: Errors from gzip and json operations are silently ignored.

While silent failure is often acceptable in crash reporting code, ignoring gz.Write and json.Marshal errors could mask data corruption. Consider at minimum checking the gzip write error.

Minimal error handling suggestion
 	var buf bytes.Buffer
 	gz := gzip.NewWriter(&buf)
-	gz.Write([]byte(plaintext))
-	gz.Close()
+	if _, err := gz.Write([]byte(plaintext)); err != nil {
+		return plaintext // fallback to uncompressed
+	}
+	if err := gz.Close(); err != nil {
+		return plaintext
+	}

187-194: Empty error handling block is a code smell.

The if sendErr != nil block is empty. Either remove the error check entirely or use _ assignment to make the intentional discard explicit.

Cleaner silent discard
 	go func() {
 		ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
 		defer cancel()
-		if sendErr := sendToNostr(ctx, payload); sendErr != nil {
-			// Silent failure - don't crash the app due to reporting
-		}
+		_ = sendToNostr(ctx, payload) // Silent failure - don't crash the app
 	}()
react-native/src/index.ts (2)

222-225: Confusing fallback logic in beforeSend handling.

The finalPayload || payload fallback on line 225 is confusing. If beforeSend returns a valid payload, it should be used directly. The || payload fallback only triggers if finalPayload is falsy (e.g., empty object), which shouldn't happen given the null check on line 223.

Clearer logic
-  const finalPayload = config.beforeSend === undefined ? payload : config.beforeSend(payload);
-  if (finalPayload === null) return;
-
-  await sendToNostr(finalPayload || payload);
+  const finalPayload = config.beforeSend ? config.beforeSend(payload) : payload;
+  if (finalPayload === null) return;
+
+  await sendToNostr(finalPayload);

243-251: ErrorUtils is untyped and may not exist in all environments.

ErrorUtils is a React Native internal API that isn't typed. Consider adding a type declaration or defensive check.

Add defensive check and type declaration
+// React Native global error handler
+declare const ErrorUtils: {
+  getGlobalHandler(): (error: Error, isFatal?: boolean) => void;
+  setGlobalHandler(handler: (error: Error, isFatal?: boolean) => void): void;
+} | undefined;

 // Install global error handler
+if (typeof ErrorUtils !== 'undefined') {
   const originalHandler = ErrorUtils.getGlobalHandler();
   ErrorUtils.setGlobalHandler((error: Error, isFatal?: boolean) => {
     captureException(error);
     if (originalHandler) {
       originalHandler(error, isFatal);
     }
   });
+}
python/bugstr/__init__.py (1)

383-385: Silent exception swallowing could hinder debugging.

While silent failure is appropriate for crash reporting (avoid meta-crashes), consider adding optional debug logging or at least a more specific exception type.

Optional: Add debug logging capability
+import logging
+
+_logger = logging.getLogger(__name__)

     except Exception:
         # Silent failure - don't crash the app
-        pass
+        _logger.debug("Failed to send crash report", exc_info=True)

The static analysis hint about try-except-pass (S110) is valid for production code, though the intentional silent failure here is documented.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1dddd39 and a50146d.

⛔ Files ignored due to path filters (1)
  • go/go.sum is excluded by !**/*.sum
📒 Files selected for processing (13)
  • README.md
  • go/README.md
  • go/bugstr.go
  • go/go.mod
  • python/.gitignore
  • python/README.md
  • python/bugstr/__init__.py
  • python/pyproject.toml
  • react-native/.gitignore
  • react-native/README.md
  • react-native/package.json
  • react-native/src/index.ts
  • react-native/tsconfig.json
🧰 Additional context used
🧬 Code graph analysis (2)
python/bugstr/__init__.py (1)
test-vectors/validate.js (1)
  • serialized (149-156)
react-native/src/index.ts (2)
android/src/main/java/com/bugstr/BugstrAnrWatcher.kt (1)
  • message (82-82)
android/bugstr-nostr-crypto/src/main/kotlin/com/bugstr/nostr/crypto/QuartzAdapters.kt (1)
  • nip44 (11-27)
🪛 OSV Scanner (2.3.1)
go/go.mod

[CRITICAL] 1-1: golang.org/x/crypto 0.28.0: Misuse of connection.serverAuthenticate may cause authorization bypass in golang.org/x/crypto

(GO-2024-3321)


[CRITICAL] 1-1: golang.org/x/crypto 0.28.0: Potential denial of service in golang.org/x/crypto

(GO-2025-3487)


[CRITICAL] 1-1: golang.org/x/crypto 0.28.0: Potential denial of service in golang.org/x/crypto/ssh/agent

(GO-2025-4116)


[CRITICAL] 1-1: golang.org/x/crypto 0.28.0: Unbounded memory consumption in golang.org/x/crypto/ssh

(GO-2025-4134)


[CRITICAL] 1-1: golang.org/x/crypto 0.28.0: Malformed constraint may cause denial of service in golang.org/x/crypto/ssh/agent

(GO-2025-4135)


[CRITICAL] 1-1: golang.org/x/crypto 0.28.0: golang.org/x/crypto/ssh/agent vulnerable to panic if message is malformed due to out of bounds read

(GHSA-f6x5-jh6r-wrfv)


[CRITICAL] 1-1: golang.org/x/crypto 0.28.0: golang.org/x/crypto Vulnerable to Denial of Service (DoS) via Slow or Incomplete Key Exchange

(GHSA-hcg3-q754-cr77)


[CRITICAL] 1-1: golang.org/x/crypto 0.28.0: golang.org/x/crypto/ssh allows an attacker to cause unbounded memory consumption

(GHSA-j5w8-q4qc-rx2x)


[CRITICAL] 1-1: golang.org/x/crypto 0.28.0: Misuse of ServerConfig.PublicKeyCallback may cause authorization bypass in golang.org/x/crypto

(GHSA-v778-237x-gjrc)

🪛 Ruff (0.14.11)
python/bugstr/__init__.py

44-51: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)


152-152: Avoid specifying long messages outside the exception class

(TRY003)


171-171: Avoid specifying long messages outside the exception class

(TRY003)


246-246: Do not catch blind exception: Exception

(BLE001)


277-277: Standard pseudo-random generators are not suitable for cryptographic purposes

(S311)


376-377: try-except-pass detected, consider logging the exception

(S110)


376-376: Do not catch blind exception: Exception

(BLE001)


383-385: try-except-pass detected, consider logging the exception

(S110)


383-383: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (20)
python/.gitignore (1)

1-6: LGTM! Standard Python ignore patterns.

The .gitignore file includes appropriate patterns for Python development artifacts (cache, bytecode, packaging, and build outputs).

python/README.md (1)

1-121: Excellent documentation for the Python SDK.

The README is comprehensive and well-organized, covering installation, multiple usage patterns (basic, manual, server mode, confirmation), framework integrations (Django, Flask), features, and configuration. The examples are clear and idiomatic.

go/README.md (1)

1-102: Well-structured documentation for the Go SDK.

The README effectively covers installation, usage patterns (including Go-specific goroutine recovery), manual capture, server modes, and configuration. The examples are idiomatic and the structure mirrors the Python SDK documentation for consistency.

README.md (3)

3-3: Clear and concise tagline update.

The updated tagline effectively emphasizes the zero-infrastructure and no-SaaS-cost value proposition.


5-5: Simplified and clearer description.

The reworded description better explains the NIP-17 encrypted messaging and user consent flow without unnecessary jargon.


16-19: Platform expansion reflects new SDK additions.

The platform table correctly documents the new Rust, Go, Python, and React Native SDKs introduced in this PR.

go/go.mod (1)

1-25: Update go 1.23.1 to match the development environment.

The go.mod declares go 1.23.1, but your current environment runs go 1.25.1. Update the module directive to go 1.25.1 to reflect the actual toolchain in use. Additionally, ensure your CI/CD workflows explicitly specify the Go version to prevent consistency issues across environments.

react-native/.gitignore (1)

1-3: LGTM!

The essential ignores are covered. Optionally, you could add .DS_Store, coverage/, and *.tsbuildinfo for more complete coverage, but these basics are sufficient.

react-native/README.md (1)

1-128: LGTM!

The documentation is comprehensive and well-structured, covering installation, usage patterns, configuration options, and payload format. The code examples are clear and follow good practices.

python/pyproject.toml (1)

22-25: Update nostr-sdk constraint to a more recent baseline version.

nostr-sdk>=0.32.0 allows versions from 0.32.0 onwards, but the latest available is 0.44.0. The lower bound constraint (0.32.0) is 12 minor versions behind current, indicating the baseline hasn't been updated. Consider updating to at least >=0.40.0 or higher if compatible with the implementation.

secp256k1>=0.14.0 is already current (0.14.0 is the latest release).

Verify compatibility with newer nostr-sdk releases before updating the constraint.

react-native/package.json (1)

43-45: No issues found. Version 2.10.0 of nostr-tools exists on npm and supports both NIP-17 and NIP-44 (NIP-17 was added in v2.9.x, and v2.10.0 switched to NIP-44 for encryption).

go/bugstr.go (3)

309-320: Rumor ID computation follows NIP-01 correctly.

The serialization format [0, pubkey, created_at, kind, tags, content] aligns with the NIP-01 specification and matches the pattern in test-vectors/validate.js.


362-378: Relay publishing logic looks correct.

The pattern of attempting each relay, returning on first success, and tracking the last error is appropriate for best-effort delivery.


257-262: No action needed—Go 1.23.1 automatically seeds math/rand.

This function is safe as-is. Go 1.20 introduced automatic seeding of math/rand, so explicit seeding is not required. The project targets Go 1.23.1, which is well after that change.

react-native/src/index.ts (2)

80-89: NIP-19 decoding implementation is correct.

The use of nip19.decode with type checking for 'npub' is the proper approach.


303-327: Error Boundary implementation is correct.

The component properly implements getDerivedStateFromError and componentDidCatch, integrates with captureException, and supports flexible fallback rendering.

python/bugstr/__init__.py (4)

337-346: Rumor ID computation correctly uses compact JSON serialization.

The use of json.dumps(..., separators=(",", ":")) produces the compact JSON format required by NIP-01, matching the pattern in test-vectors/validate.js.


238-248: NIP-19 decoding using PublicKey.from_bech32 is correct.

This is the appropriate nostr-sdk method for decoding npub bech32 strings. The broad exception catch is acceptable here as invalid pubkeys should fail gracefully.


273-278: Use of random.randint is acceptable for timestamp fuzzing.

The static analysis warning (S311) flags this as unsuitable for cryptographic purposes, but timestamp obfuscation for privacy is not a cryptographic operation. The current usage is appropriate.


371-381: Client relay publishing pattern is reasonable.

The connect/send/disconnect pattern wrapped in the outer try-except provides adequate error handling for best-effort crash report delivery.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@alltheseas
Copy link
Owner Author

@coderabbitai review please

@coderabbitai
Copy link

coderabbitai bot commented Jan 16, 2026

@alltheseas: I'll review the changes in this PR right away.


🧠 Learnings used
Learnt from: CR
Repo: alltheseas/bugstr PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T08:05:25.480Z
Learning: Applies to {android,typescript}/**/*.{kt,ts,tsx} : Rumor events in NIP-17/59 implementation must include `sig: ""` (empty string, not omitted) and computed `id` field (SHA256 of serialized event)
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

alltheseas and others added 4 commits January 16, 2026 08:48
- Panic hook for automatic crash capture
- NIP-17 gift wrap encryption with NIP-44
- Gzip compression for payloads over 1KB
- Configurable relays, redaction patterns, consent hooks

Closes #6

Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Global sys.excepthook integration
- NIP-17 gift wrap encryption with NIP-44
- Gzip compression for payloads over 1KB
- Configurable relays, redaction patterns, before_send/confirm_send hooks

Closes #7

Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- ErrorBoundary component for declarative error handling
- Global ErrorUtils handler integration
- Native Alert consent dialog (configurable)
- NIP-17 gift wrap encryption with proper NIP-01 rumor id
- tsconfig.build.json for react-native-builder-bob

Closes #8

Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Go, Python, React Native to platforms table
- Document size limits and compression threshold (1KB)
- Document gzip envelope format for large payloads
- Add compression efficiency table (70-90% reduction)

Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@alltheseas alltheseas force-pushed the feat/multi-platform-sdks branch from 8928ab8 to 0950697 Compare January 16, 2026 14:49
alltheseas and others added 2 commits January 16, 2026 09:09
- Add relay.primal.net (1MB limit) as secondary relay
- Document default relays with max message sizes in README
- Add relay size compatibility table (64KB=99%, 128KB=90%)
- Document 60KB practical limit for universal delivery
- Update all SDKs to use: damus (1MB), primal (1MB), nos.lol (128KB)

Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Android/Kotlin tested with Zapstore integration (PR #272)
- Other platforms marked as "Guinea pigs needed"

Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@go/bugstr.go`:
- Around line 201-213: In decodePubkey, the unchecked assertion data.(string)
can panic if nip19.Decode returns a non-string; change the code to perform a
safe type assertion (e.g., s, ok := data.(string)) after calling nip19.Decode
and only return s when ok is true, otherwise return an empty string or handle
the error path; update the branch that checks prefix == "npub" and the result of
nip19.Decode accordingly to avoid panics.

In `@react-native/src/index.ts`:
- Around line 215-236: The confirmation is showing the original payload because
config.beforeSend is currently applied after confirmSend/nativeConfirm; update
maybeSend so it first calls config.beforeSend (awaiting it and handling a null
return to abort) and uses the potentially modified payload to build the
BugstrSummary and pass into config.confirmSend/nativeConfirm, then call
sendToNostr with that same modified payload (or abort if beforeSend returned
null); reference maybeSend, config.beforeSend, config.confirmSend,
nativeConfirm, and sendToNostr to locate the changes.
- Around line 159-170: The nip44.encrypt function is synchronous (returns a
string), so remove the erroneous await operators where it's called: change the
call that assigns sealContent (currently awaiting
nip44.encrypt(JSON.stringify(unsignedKind14), conversationKey)) to a direct
call, and likewise remove await for the call that assigns giftWrapContent
(currently awaiting nip44.encrypt(JSON.stringify(seal), wrapKey)); keep all
other logic (conversationKey, wrapKey, unsignedKind14, seal) unchanged.

In `@README.md`:
- Around line 33-43: The "Default Relays" table in README.md contains incorrect
max message sizes; update the entries for `wss://relay.damus.io` and
`wss://relay.primal.net` to reflect strfry defaults (events.maxEventSize = 65536
bytes and relay.maxWebsocketPayloadSize = 131072 bytes) — i.e. document the
practical limit as ~64–128 KB rather than 1 MB — keep `wss://nos.lol` as 128 KB,
and adjust the Notes column to mention these strfry defaults and that actual
usable event size is typically 64 KB (with websocket payload up to 128 KB).
🧹 Nitpick comments (5)
python/bugstr/__init__.py (2)

273-278: Non-cryptographic random used for timestamp obfuscation.

random.randint is flagged by static analysis (S311) for cryptographic use. However, this is used solely for timestamp jittering to provide metadata privacy, not for cryptographic key generation. The security risk is minimal since predictable timestamps don't compromise message confidentiality.

Consider documenting the intent or using secrets.randbelow if stricter compliance is desired.


371-381: Silent failures aligned with SDK design philosophy.

The broad except Exception: pass patterns (lines 376-377 and 383-385) are intentional per the SDK's "silent failure" design to avoid crashing the host application. While static analysis flags these (S110, BLE001), they're appropriate for crash reporting infrastructure.

Consider adding debug-level logging gated by an environment variable for troubleshooting relay connectivity issues.

go/bugstr.go (2)

257-262: Non-cryptographic random used for timestamp jittering.

rand.Int63n uses the default math/rand source, which isn't cryptographically secure. Similar to the Python implementation, this is used for metadata privacy (timestamp obfuscation), not cryptographic purposes, so the risk is minimal.

For consistency with ephemeral key generation (which uses nostr.GeneratePrivateKey), consider using crypto/rand if stricter requirements apply.


269-282: Ignored error from gzip.Write.

Line 271 ignores the error from gz.Write(). While gzip writing to an in-memory buffer rarely fails, ignoring errors violates Go best practices.

Handle gzip write error
 func maybeCompress(plaintext string) string {
 	if len(plaintext) < 1024 {
 		return plaintext
 	}

 	var buf bytes.Buffer
 	gz := gzip.NewWriter(&buf)
-	gz.Write([]byte(plaintext))
-	gz.Close()
+	if _, err := gz.Write([]byte(plaintext)); err != nil {
+		return plaintext // Fallback to uncompressed
+	}
+	if err := gz.Close(); err != nil {
+		return plaintext
+	}

 	envelope := CompressedEnvelope{
 		V:           1,
 		Compression: "gzip",
 		Payload:     base64.StdEncoding.EncodeToString(buf.Bytes()),
 	}

 	result, _ := json.Marshal(envelope)
 	return string(result)
 }
react-native/src/index.ts (1)

253-260: Global error handler captures errors but may not await send.

The global error handler calls captureException(error) synchronously, but the actual send is async. If the app crashes immediately after, the report may not be sent. This is a known limitation of crash reporting in JavaScript environments.

Consider documenting this behavior or exploring AppState listeners to flush pending reports.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0950697 and 7951ebc.

📒 Files selected for processing (4)
  • README.md
  • go/bugstr.go
  • python/bugstr/__init__.py
  • react-native/src/index.ts
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: alltheseas/bugstr PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T08:05:25.480Z
Learning: Applies to {android,typescript}/**/*.{kt,ts,tsx} : Rumor events in NIP-17/59 implementation must include `sig: ""` (empty string, not omitted) and computed `id` field (SHA256 of serialized event)
📚 Learning: 2026-01-16T08:05:25.480Z
Learnt from: CR
Repo: alltheseas/bugstr PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T08:05:25.480Z
Learning: Applies to {android,typescript,dart}/CHANGELOG.md : All user-facing changes require a CHANGELOG.md entry in the respective platform directory (android/, typescript/, dart/) following the format: sections for Added, Changed, and Fixed under [Unreleased]

Applied to files:

  • react-native/src/index.ts
  • README.md
📚 Learning: 2026-01-16T08:05:25.480Z
Learnt from: CR
Repo: alltheseas/bugstr PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T08:05:25.480Z
Learning: Applies to {android,typescript}/**/*.{kt,ts,tsx} : Rumor events in NIP-17/59 implementation must include `sig: ""` (empty string, not omitted) and computed `id` field (SHA256 of serialized event)

Applied to files:

  • react-native/src/index.ts
📚 Learning: 2026-01-16T08:05:25.480Z
Learnt from: CR
Repo: alltheseas/bugstr PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T08:05:25.480Z
Learning: Applies to {android,typescript}/**/*.{kt,ts,tsx} : Event serialization for NIP-17 must follow the exact order: `[0, pubkey, created_at, kind, tags, content]` before computing SHA256 id hash

Applied to files:

  • react-native/src/index.ts
📚 Learning: 2026-01-16T08:05:25.480Z
Learnt from: CR
Repo: alltheseas/bugstr PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T08:05:25.480Z
Learning: Applies to {android,typescript}/**/*.{kt,ts,tsx} : Seal events in NIP-59 must be signed by the sender's key

Applied to files:

  • react-native/src/index.ts
📚 Learning: 2026-01-16T08:05:25.480Z
Learnt from: CR
Repo: alltheseas/bugstr PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T08:05:25.480Z
Learning: Applies to {android,typescript}/**/*.{kt,ts,tsx} : Event ID computation must return a lowercase hex string (64 characters) using SHA256 hash of the serialized event

Applied to files:

  • react-native/src/index.ts
📚 Learning: 2026-01-16T08:05:25.480Z
Learnt from: CR
Repo: alltheseas/bugstr PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T08:05:25.480Z
Learning: Applies to {android,typescript}/**/*.{kt,ts,tsx} : Gift wrap events in NIP-59 must be signed by an ephemeral key

Applied to files:

  • react-native/src/index.ts
🧬 Code graph analysis (1)
python/bugstr/__init__.py (5)
android/bugstr-nostr-crypto/src/main/kotlin/com/bugstr/nostr/crypto/QuartzAdapters.kt (1)
  • nip44 (11-27)
go/bugstr.go (2)
  • Config (38-63)
  • Payload (66-72)
android/src/main/java/com/bugstr/BugstrAnrWatcher.kt (1)
  • message (82-82)
react-native/src/index.ts (1)
  • init (245-261)
test-vectors/validate.js (1)
  • serialized (149-156)
🪛 Ruff (0.14.11)
python/bugstr/__init__.py

44-51: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)


152-152: Avoid specifying long messages outside the exception class

(TRY003)


171-171: Avoid specifying long messages outside the exception class

(TRY003)


246-246: Do not catch blind exception: Exception

(BLE001)


277-277: Standard pseudo-random generators are not suitable for cryptographic purposes

(S311)


376-377: try-except-pass detected, consider logging the exception

(S110)


376-376: Do not catch blind exception: Exception

(BLE001)


383-385: try-except-pass detected, consider logging the exception

(S110)


383-383: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (13)
python/bugstr/__init__.py (5)

36-41: LGTM - Graceful degradation for missing dependency.

The try/except pattern for importing nostr_sdk with the HAS_NOSTR flag allows the module to be imported without crashing, deferring the error to initialization time.


65-89: LGTM - Config dataclass is well-structured.

The configuration mirrors the Go SDK's Config struct with appropriate Python idioms. Default factory patterns for mutable defaults (lists) are correctly used.


91-109: LGTM - Payload dataclass with proper serialization.

The to_dict method correctly omits optional fields when not present, matching the Go SDK's json:",omitempty" behavior.


327-346: LGTM - Rumor construction follows NIP-01/NIP-17 correctly.

The rumor includes:

  • sig: "" (empty string, not omitted) per NIP-17 requirements
  • Computed id field via SHA-256 of [0, pubkey, created_at, kind, tags, content] with compact JSON serialization

This matches the learnings and is consistent with the Go and React Native implementations. Based on learnings, this is the correct approach.


350-357: API usage is correct for nostr-sdk>=0.32.0.

The EventBuilder API with custom_created_at() chained to to_event() is valid and documented for the version specified in the project (nostr-sdk>=0.32.0). The pattern is confirmed in Python nostr-sdk examples and the method chain is properly supported.

go/bugstr.go (3)

298-320: LGTM - Rumor construction follows NIP-01/NIP-17 correctly.

The rumor includes:

  • sig: "" (empty string, not omitted) at line 306
  • Computed id via SHA-256 of [0, pubkey, created_at, kind, tags, content] at lines 309-320

This correctly implements the NIP-01 event ID computation and aligns with the retrieved learnings. Based on learnings, this is the correct serialization order.


362-376: LGTM - Relay publishing with fail-fast on success.

The relay iteration correctly:

  • Attempts each relay sequentially
  • Returns immediately on first successful publish
  • Properly closes relay connections
  • Tracks the last error for reporting

This is a reasonable strategy for crash reporting where delivery to any single relay suffices.


333-339: No action required—seal event signing is correct.

The Sign method in go-nostr v0.42.0 automatically populates the PubKey field by deriving it from the provided private key. Calling seal.Sign(senderPrivkey) correctly sets the PubKey, ID, and signature fields. The code follows the standard pattern and complies with NIP-59 requirements for seal events.

Likely an incorrect or invalid review comment.

react-native/src/index.ts (2)

141-157: LGTM - Rumor ID computation addressed correctly.

The implementation now:

  • Uses getEventHash(rumorEvent) from nostr-tools to compute the rumor ID per NIP-01
  • Includes sig: '' (empty string) per NIP-17 requirements

This addresses the previous review comment and aligns with the retrieved learnings. Based on learnings, this correctly implements rumor event requirements.


313-337: LGTM - ErrorBoundary implementation is correct.

The ErrorBoundary:

  • Properly implements getDerivedStateFromError and componentDidCatch
  • Captures errors with component stack info
  • Supports both static fallback and render-prop fallback patterns
  • Calls optional onError callback for custom handling

This is a well-structured React error boundary pattern.

README.md (3)

11-19: LGTM - Platform table updated correctly.

The table now includes all new SDKs (Go, Python, React Native) with appropriate status indicators and directory links. The "Guinea pigs needed" notation clearly indicates testing status.


57-60: Compression threshold matches implementation.

The documentation states payloads < 1 KB are sent as plain JSON, ≥ 1 KB are compressed. This matches the maybeCompress functions in both Go (line 265: len(plaintext) < 1024) and Python (line 283: len(plaintext.encode()) < 1024).


97-103: LGTM - Critical NIP-17 requirements documented.

The documentation correctly specifies that rumors must include:

  • id computed as SHA256 of the serialization array
  • sig: "" as empty string (not omitted)

This aligns with the retrieved learnings and helps future contributors avoid compliance issues. Based on learnings, this is accurate documentation.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

alltheseas and others added 3 commits January 16, 2026 09:22
Upgrades the Dart SDK from skeleton to full implementation:

- Add BugstrConfig with default relays (damus, primal, nos.lol)
- Add CrashPayload with auto-redaction for sensitive data
- Add Bugstr client with NIP-17 gift-wrapped crash reporting
- Use ndk package for NIP-44 encryption and NIP-59 gift wrap
- Auto-install Flutter error handlers on init()
- Support beforeSend and confirmSend hooks
- Random timestamps within ±2 days for privacy

Signed-off-by: alltheseas
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: alltheseas
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Go, Python, React Native, Rust to supported platforms table,
project structure, and documentation format guidelines.

Signed-off-by: alltheseas
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@alltheseas alltheseas changed the title fix(go): Fix NIP-19 decoding and rumor ID computation add go, python, react native, flutter libraries Jan 16, 2026
@alltheseas
Copy link
Owner Author

@coderabbitai please review latest commits

@coderabbitai
Copy link

coderabbitai bot commented Jan 16, 2026

@alltheseas: I'll review the latest commits right away.


🧠 Learnings used
Learnt from: CR
Repo: alltheseas/bugstr PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T08:05:25.480Z
Learning: Applies to {android,typescript}/**/*.{kt,ts,tsx} : Rumor events in NIP-17/59 implementation must include `sig: ""` (empty string, not omitted) and computed `id` field (SHA256 of serialized event)

Learnt from: CR
Repo: alltheseas/bugstr PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T08:05:25.480Z
Learning: Applies to {android,typescript,dart}/**/*.{kt,ts,tsx,dart} : All code must be reviewable by human developers with clear, descriptive variable and function names, appropriate comments for non-obvious logic, and consistent formatting per language conventions

Learnt from: CR
Repo: alltheseas/bugstr PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T08:05:25.480Z
Learning: Applies to {android,typescript}/**/*.{kt,ts,tsx} : Seal events in NIP-59 must be signed by the sender's key
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Go:
- Use safe type assertion in decodePubkey to prevent panic

React Native:
- Remove erroneous await from synchronous nip44.encrypt calls
- Apply beforeSend hook before confirmSend so confirmation shows
  the modified payload

Docs:
- Correct relay size limits to strfry defaults (64KB event,
  128KB websocket) instead of incorrect 1MB values

Signed-off-by: alltheseas
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@alltheseas alltheseas merged commit 558e690 into main Jan 16, 2026
2 checks passed
@alltheseas alltheseas mentioned this pull request Jan 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant