Commit Graph

271 Commits

Author SHA1 Message Date
Vitor Pamplona 280f21159f v1.10.0 2026-05-16 16:53:00 -04:00
Vitor Pamplona 8a498695a9 v1.09.2 2026-05-16 12:06:32 -04:00
Vitor Pamplona 5a30b10a77 v1.09.1 2026-05-15 18:25:17 -04:00
Vitor Pamplona 74c0d6906e v1.09.0 2026-05-15 15:43:47 -04:00
Vitor Pamplona d60dcf9c79 update dependencies 2026-05-14 12:06:55 -04:00
Vitor Pamplona 6ab077361f updates dependencies 2026-05-12 22:14:06 -04:00
davotoula 91aa72ae1e Rip DLNA code paths, collapse to Chromecast-only
- chore(cast): drop protocol-toggle settings, strings, perms, deps
- gate cast on play flavor via BuildConfig.IS_CASTING_AVAILABLE
- fix(cast): keep local player paused across recompose; disable transport while casting
2026-05-11 16:35:47 +02:00
Claude 5df0e2a37a Add LAN video casting (Chromecast on play, DLNA on both flavors) v1
fix(cast): handle every Chromecast session terminal state and HLS mime hint
fix(cast): repair DLNA Res ctor and add play-only protocol toggle
fix(cast): backfill Cast button for accounts with pre-existing video settings
fix(cast): include cast glyphs in symbol font subset and add diagnostic logs
fix(cast): wire jUPnP's required Jetty deps and survive dialog dismissal
fix(cast): add jetty-client so jUPnP can issue UPnP control requests
2026-05-11 16:35:47 +02:00
Vitor Pamplona faf04fa1e3 update dependencies 2026-05-08 17:06:28 -04:00
Claude e214143def feat(relay): TOML config file (--config /path/to/relay.toml)
Adds operator-facing TOML configuration to :quartz-relay, with the
section layout deliberately mirroring nostr-rs-relay's config.toml so
existing operators can port across with little churn.

Sections parsed AND enforced today:
  [info]      — NIP-11 doc fields (name, description, contact, pubkey,
                 software, supported_nips, …); replaces the previous
                 hardcoded RelayInfo.default()
  [network]   — host, port, path
  [database]  — in_memory toggle + file path
  [options]   — verify_signatures, require_auth (compose to the right
                 IRelayPolicy stack)

Sections parsed today but NOT YET ENFORCED (forward-compat for the
upcoming rate-limit / authorization work — relay logs a warning when
they're set):
  [limits]         — max_event_bytes, messages_per_sec, …
  [authorization]  — pubkey_whitelist/blacklist, kind_whitelist/blacklist
  [options].reject_future_seconds
  [network].remote_ip_header

CLI flag precedence over the config file is preserved: --host, --port,
--path, --info, --db, --auth, --verify all override the matching field.

Adds:
  - cc.ekblad:4koma 1.2.0 for TOML parsing
  - quartz-relay/config.example.toml as the canonical operator reference
  - 5 unit tests (defaults, full parse, NIP-11 mapping, bundled example
    file, optional sections)
2026-05-07 01:21:58 +00:00
Claude eb80dbcd15 feat(quartz-relay): rename + promote to a real Nostr relay
Renames :quartz-test-relay to :quartz-relay and promotes it from a
test-only fixture to a real, runnable Nostr relay that just happens
to also be used in tests.

Two transports now share the same `Relay` core:

- `RelayHub` + `InProcessWebSocket` — no socket, fastest path; ideal
  for unit tests inside one JVM.
- `LocalRelayServer` — Ktor `embeddedServer` (CIO engine) listening
  on a real `ws://` port. Use for `cli` interop tests, Android
  instrumented tests, or running the relay standalone.

NIPs implemented (all driven through production `NostrClient` in the
new `LocalRelayServerTest`):

- NIP-01 wire protocol (REQ/EVENT/EOSE/CLOSE) over real WebSockets
- NIP-09 deletion (via existing `DeletionRequestModule`)
- NIP-11 relay info doc — `RelayInfo` wraps the existing
  `Nip11RelayInformation` model and is served on HTTP GET when
  `Accept: application/nostr+json` is requested. Loadable from a
  JSON config file via `RelayInfo.fromFile(...)`.
- NIP-40 expiration (existing `ExpirationModule`)
- NIP-42 AUTH (existing `FullAuthPolicy`, opted-in via the
  `--auth` flag in the standalone runner)
- NIP-45 COUNT
- NIP-50 search via the existing FTS index
- NIP-62 right-to-vanish (existing module)

Adds a standalone runner: `./gradlew :quartz-relay:run --args="--port 7447 --verify"`
binds the relay to a real port. Flags: --host, --port, --path,
--info <file>, --db <file>, --auth, --verify.

Test-only event generators moved to a clearly-named
`com.vitorpamplona.quartz.relay.fixtures` package so they're still
shareable with consumer tests without polluting the production API.

Adds `LocalRelayServerTest` covering NIP-01/11/42/45/50 over a real
loopback WebSocket. Existing 11-test `Nip01ComplianceTest` (in-process
transport) continues to pass.
2026-05-07 00:56:57 +00:00
Vitor Pamplona 5bf542c18d Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2026-05-06 10:55:17 -04:00
Vitor Pamplona 85d27a80b0 Updates AGP 2026-05-06 10:53:14 -04:00
Claude 614e403037 chore(deps): bump schnorr256k1-kmp to 1.0.5 2026-05-06 14:31:28 +00:00
Claude ecb985e2b4 build(quartz): bump schnorr256k1-kmp to 1.0.3 with bundled JVM JNI
Pulls in the per-platform JNI shards (linux-x86_64, linux-aarch64,
darwin-x86_64, darwin-aarch64) that the upstream library now publishes as
runtime dependencies of schnorr256k1-kmp-jvm. The 1.0.3 release also
moved the groupId to com.vitorpamplona.schnorr256k1.

Net effect: Secp256k1CrossValidationTest's customCImplementationMatchesAcinqAndKotlin
no longer prints SKIP, and the Custom C(JNI) column in
Secp256k1TripleBenchmark now reports numbers (verifySchnorr ~25.6k ops/s,
signSchnorr cached pk ~57.4k ops/s) instead of (N/A). 188/188 tests pass
in the secp256k1 suite on a stock JVM with no LD_LIBRARY_PATH or
-Djava.library.path needed.

https://claude.ai/code/session_01XEfLBStDum7h8Brwo7qFXt
2026-04-28 20:20:28 +00:00
Claude 2ad1a48123 refactor(quartz): migrate in-tree C secp256k1 to libschnorr256k1-kmp
The custom C secp256k1 implementation under quartz/src/main/c/ was never
wired into Gradle (no externalNativeBuild block) and only existed for the
3-way benchmark + cross-validation test against ACINQ on JVM/Android.
The C library has been split out to vitorpamplona/libschnorr256k1{,-kmp},
so the in-repo copy is dead weight.

This change replaces it with the published Maven Central artifact
`com.vitorpamplona:schnorr256k1-kmp:1.0.0`, scoped to test/benchmark
configurations only — production crypto continues to use ACINQ
secp256k1-kmp on JVM/Android and the pure-Kotlin Secp256k1 on native.

The Android AAR ships libschnorr256k1_jni.so for arm64-v8a and x86_64,
so the Android benchmark now exercises the C row automatically (no more
manual build_android.sh). On JVM the .so still has to be installed by the
developer; the cross-validation test and triple-benchmark gracefully skip
the C row when System.loadLibrary fails, matching prior behavior.

Verified on JVM: all 13 secp256k1 jvmTest classes pass (188 tests), and
ACINQ vs pure-Kotlin benchmark numbers match the documented baseline
within sandbox noise (verifySchnorr ~15k ops/s, signSchnorr cached
~33k ops/s, pubkeyCreate+Compress ~36k ops/s).

Removes ~5,100 LOC: quartz/src/main/c/ (4,500), Secp256k1InstanceC.*
expect/actual shim (560), Secp256k1C JNI declaration object.

https://claude.ai/code/session_01KnvpK2amcVZKfFiZJvHjVe
2026-04-28 15:36:25 +00:00
Vitor Pamplona af6b8c8a52 Removes old MLKit labeler because of the terrible labels it was creating. 2026-04-27 11:29:28 -04:00
Claude 952e0a2192 feat(ai): prefer genai image description, fall back to image labeling
Adds com.google.mlkit:genai-image-description as the primary alt-text
source — Gemini Nano via AICore produces full descriptive sentences on
supported devices. When checkFeatureStatus reports anything other than
AVAILABLE (or AICore is missing), the service falls back to the legacy
play-services-mlkit-image-labeling keyword join. Both paths sit behind
the same MLKitImageLabelService.suggestAltText API; the F-Droid stub is
unchanged.
2026-04-27 13:32:57 +00:00
Claude d8cb5c66ec feat(ai): suggest alt-text via on-device image labeling
Wire ML Kit image labeling into the media-attach dialog so the alt-text
field is prefilled with a confidence-filtered, comma-separated label
list when the user picks an image and the field is still empty. A
spinner shows during labeling and a dismissible "AI-suggested, edit me"
chip lets the user revert. Play flavor uses
play-services-mlkit-image-labeling; F-Droid ships a no-op stub.
2026-04-27 13:12:00 +00:00
Vitor Pamplona 75fb4845e5 Update AGP and dependencies 2026-04-23 19:15:12 -04:00
Claude ae548a851d feat: add always-on notification relay service
Implements a 5-layer always-on notification system that maintains
persistent WebSocket connections to the user's inbox relays for
real-time notification delivery, eliminating the dependency on the
external push server seeing all relays.

Layer architecture:
- L1: Foreground service (specialUse type, no Android 15 time limit)
  keeps the shared NostrClient alive by collecting relayServices flow
- L2: FCM/UnifiedPush (existing, unchanged) as wakeup trigger
- L3: WorkManager periodic worker (15-min catch-up safety net)
- L4: BOOT_COMPLETED receiver (restart service after reboot)
- L5: AlarmManager watchdog (5-min health check for OEM killers)

Key design: the foreground service shares the same NostrClient as the
UI. When the app foregrounds, both the UI and service are subscribers
to the relay pool. When the app backgrounds, UI subscriptions drop
but service subscriptions remain — zero reconnection needed.

New files:
- NotificationRelayService: foreground service with persistent notification
- BootCompletedReceiver: restarts service on device boot
- NotificationCatchUpWorker: WorkManager fallback for missed events
- ServiceWatchdogManager: AlarmManager-based health monitor
- AlwaysOnNotificationServiceManager: coordinates all 5 layers

Settings: opt-in toggle in App Settings, persisted per-account.

https://claude.ai/code/session_01LEPfmgGnwjB9a5SDFw5U8t
2026-04-21 23:24:10 +00:00
Vitor Pamplona d7cc99b196 update dependencies 2026-04-20 10:51:40 -04:00
Vitor Pamplona f5f8bfd7dc Merge pull request #2430 from nrobi144/feat/desktop-multiplatform-distribution
feat(desktop): multi-platform distribution — 8 assets, Homebrew + Winget
2026-04-17 08:19:40 -04:00
davotoula f41c062be4 lightcompressor-enhanced patch upgrade, fix for gif2mp4 2026-04-16 14:33:47 +02:00
nrobi144 da1037423c feat(desktop): version source-of-truth + RPM + AppImage packaging
Phase 1+2 of multi-platform distribution plan:
- gradle/libs.versions.toml: add `app = "1.08.0"` as single source of truth
- Root build.gradle: allprojects { version = libs.versions.app.get() }
- amethyst/build.gradle: versionName from catalog (versionCode stays local)
- desktopApp/build.gradle.kts: drop hardcoded "1.0.0"; inherit project.version;
  add TargetFormat.Rpm; add linux DSL (menuGroup, appCategory, debMaintainer,
  rpmLicenseType, rpmPackageVersion with dashes stripped)
- desktopApp/build.gradle.kts: new createReleaseAppImage task wrapping
  createReleaseDistributable with linuxdeploy (TargetFormat.AppImage is
  broken in Compose 1.10.x — CMP-7101)
- packaging/appimage/: AppRun launcher (sets LD_LIBRARY_PATH for bundled VLC),
  amethyst.desktop XDG entry, 512x512 icon extracted from icon.icns
- scripts/asset-name.sh: single source for release asset naming contract
2026-04-16 14:35:01 +03:00
davotoula fee6607c38 chore(hls): bump lightcompressor-enhanced to released 2.2.0
Moves off the 2.1.1-hls-SNAPSHOT mavenLocal iteration onto the
released JitPack artifact. API surface is identical to the final
SNAPSHOT Amethyst was already running against after the library
code review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 15:33:48 +02:00
davotoula 90235e91f2 refactor(hls): upgrade to lightcompressor-enhanced 2.1.1-hls-SNAPSHOT
Collapses Amethyst's hand-rolled HLS orchestration onto the library's
HlsUploadHelper.run. The library now ships everything the prior session
had to reimplement: per-rendition width/height/codec metadata on the
onRenditionComplete callback, a public PlaylistRewriter, canonical
HlsContentTypes constants, and the transcode -> upload -> rewrite loop
itself.

- bump libs.versions.toml to 2.1.1-hls-SNAPSHOT
- delete HlsUploadPipeline, HlsBundle, HlsTranscoder, HlsTranscodingSession,
  HlsPlaylistRewriter and their tests; HlsUploadHelper.run + the library
  rewriter cover everything they did
- rewrite HlsVideoEventBuilder to consume HlsRenditionSummary width/height
  directly; drops the master-playlist streamInfRegex entirely
- HlsPublishOrchestrator now wraps HlsUploadHelper.run: a SimpleHlsListener
  drives Transcoding progress while the uploader lambda captures each
  MediaUploadResult in a side-channel map keyed by the library's
  suggestedFilename, so per-rendition sha256/size still flow into the
  NIP-71 imeta tags
- extract HlsBlobUploader into its own file (was inline in the deleted
  HlsUploadPipeline.kt)

Net delta on HLS code: 3194 -> 2182 lines (-1012, ~32%).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 15:33:48 +02:00
davotoula 80b7b38b27 feat(hls): add HlsPlaylistRewriter for post-upload URL substitution
Pure rewriter that walks HLS master or media playlists line-by-line and
substitutes each resource reference (segment file, EXT-X-MAP init,
variant media.m3u8) with its uploaded absolute URL. Preserves every
#EXT-X-STREAM-INF, #EXTINF and other directive line verbatim so the
BANDWIDTH/RESOLUTION/CODECS attributes that ExoPlayer's
AdaptiveTrackSelection reads stay intact. Loud failure on missing
entries in the URL map to avoid silent data loss.

Bumps lightcompressor-enhanced to 2.1.0 for the HlsPreparer API that
the following milestones will wrap.

Milestone 1 of the HLS video sharing plan (2026-04-13).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-15 15:29:48 +02:00
Vitor Pamplona fc0e24faba Merge pull request #2382 from davotoula/feat/lightcompressor-v2
Update LightCompressor-enhanced to v2.0.0
2026-04-13 17:38:19 -04:00
Vitor Pamplona c00222fa5b Merge branch 'main' of https://github.com/vitorpamplona/amethyst
# Conflicts:
#	gradle/libs.versions.toml
2026-04-13 17:28:28 -04:00
Vitor Pamplona 117ba4318b Updates AGP and other dependencies 2026-04-13 17:26:36 -04:00
davotoula 77cd51ec86 update LightCompressor-enhanced to v2.0.0
Migrates imports from com.abedelazizshe.lightcompressorlibrary to
com.davotoula.lightcompressor due to package rename in the fork.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-13 23:23:02 +02:00
nrobi144 7cad30b899 feat(tor): add kmp-tor desktop daemon + preferences
Phase 2: Desktop Tor runtime implementation.

DesktopTorManager:
- Implements ITorManager using kmp-tor (Apache 2.0)
- Reactive: auto-starts/stops Tor based on TorType flow
- Supports INTERNAL (embedded kmp-tor) and EXTERNAL (user SOCKS port)
- NEWNYM/Dormant/Active signal support via TorCmd
- OS-specific data dirs (~/Library/Application Support, ~/.local/share, %APPDATA%)
- Directory permissions set to 700
- stopSync() for shutdown hook integration

DesktopTorPreferences:
- Implements ITorSettingsPersistence using java.util.prefs.Preferences
- Underscore-separated keys matching project convention
- Default TorType.OFF (user must opt-in)
- No explicit flush() (matches existing DesktopPreferences pattern)

Gradle:
- kmp-tor runtime 2.6.0 + resource-exec-tor 409.5.0 in version catalog
- java.management module added to nativeDistributions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:43 +03:00
davotoula 9a02ee14ce refactor: use GifToMp4Converter from LightCompressor-enhanced 1.9.0
LightCompressor-enhanced 1.9.0 bundles the GIF-to-MP4 converter originally
contributed upstream from amethyst. Drop the duplicated copies and delegate
to the library, adapting GifToMp4Result -> MediaCompressorResult.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 21:31:13 +02:00
Claude fa0da7fae3 feat: upgrade negentropy-kmp to v1.0.2, enable macosArm64 target
negentropy-kmp v1.0.2 now publishes a macosArm64 artifact, unblocking
the macOS native target. Wired macosMain/macosTest source sets through
appleMain/appleTest and updated run_all.sh to run the K/Native
benchmark on both Linux (linuxX64) and macOS (macosArm64).

https://claude.ai/code/session_01WSNE6QKiYM2ZutQD2UihCW
2026-04-11 00:44:33 +00:00
davotoula 6a3e0b0cb6 update video compressor library 2026-04-10 11:50:53 +02:00
Vitor Pamplona 5cef0c1c46 updates versions 2026-04-08 21:27:14 -04:00
Claude 7ae536e80d feat: add AI writing help to new post screen
Add on-device AI writing assistance using ML Kit GenAI APIs (Rewriting,
Proofreading) powered by Gemini Nano via Android AICore. Users can tap
the sparkle button in the compose toolbar to access tone chips (Correct,
Rephrase, Shorter, Elaborate, Friendly, Professional, More Direct,
Punchy, + Emoji) that transform their post text. Results appear in a
card with Use/Dismiss actions.

- Play flavor: MLKitWritingAssistant using on-device Gemini Nano
- F-Droid flavor: NoOpWritingAssistant stub (returns unavailable)
- WritingAssistant interface for future DVM/NIP90 integration
- AI button hidden on unsupported devices

Also fixes pre-existing exhaustive when expression in
DecryptAndIndexProcessor for GroupEventResult.Duplicate.

https://claude.ai/code/session_01RbCYGrbbapRMike8WQy41F
2026-04-08 12:47:23 +00:00
Claude 12b2731eb5 feat: add WebRTC voice/video call infrastructure
Implements P2P calling over Nostr relays using WebRTC for media
transport and NIP-59 Gift Wraps for encrypted signaling. No custom
server required — only public STUN servers for NAT traversal.

Protocol layer (quartz/nip100WebRtcCalls):
- 6 new event kinds (25050-25055): offer, answer, ICE candidate,
  hangup, reject, renegotiate
- WebRtcCallFactory for creating and gift-wrapping signaling events
- CallIdTag and CallTypeTag for event metadata
- Events registered in EventFactory

Call state machine (commons/call):
- CallState sealed interface with full lifecycle states
- CallManager orchestrating signaling and state transitions
- Follow-gate spam prevention: only followed users can ring,
  non-follows are silently ignored

Android WebRTC integration (amethyst/service/call):
- WebRtcCallSession wrapping Google WebRTC PeerConnection
- CallForegroundService for keeping calls alive in background
- IceServerConfig with default public STUN servers
- User-configurable TURN server support

Android UI (amethyst/ui/call):
- CallScreen with offering, connecting, connected, and ended states
- IncomingCallUI with accept/reject buttons
- ConnectedCallUI with mute, video toggle, speaker, and timer
- Call button added to 1-on-1 DM chat header
- ActiveCall route added to navigation

https://claude.ai/code/session_017hZm7yu7CzmcQgZGSaqSXS
2026-04-01 23:28:39 +00:00
Vitor Pamplona 0f7bcdba38 Redesigns the GeoHash library for performance. 2026-04-01 18:01:39 -04:00
Claude e50ae0fb1e feat: replace arti-mobile-ex with custom-built Arti native library
The Guardian Project's arti-mobile-ex AAR has three problems:
1. No 16KB page-aligned binaries (required for Google Play)
2. ArtiProxy's stop()+start() causes state file lock conflicts
   (lock is tied to TorClient object lifetime, released only on GC)
3. ~140MB AAR size

Replace with a custom JNI bridge built from Arti source, following
BitChat's proven approach:

Build tooling (tools/arti-build/):
- build-arti.sh: Clones official Arti, compiles with cargo-ndk
  for ARM64 + x86_64, NDK 25+ for 16KB page alignment
- Cargo.toml: Minimal deps with size-optimized release profile
- src/lib.rs: Custom SOCKS5 proxy with proper lifecycle:
  - initialize() creates TorClient once (holds state lock forever)
  - startSocksProxy() binds port and accepts connections
  - stopSocksProxy() aborts listener only (TorClient stays alive)
  This cleanly separates "stop routing traffic" from "destroy client"

Kotlin side:
- ArtiNative.kt: JNI declarations + ArtiLogCallback interface
- TorService.kt: Uses ArtiNative directly, start() initializes +
  starts proxy, stop() only stops proxy (no lock issues)
- TorManager.kt: Restored stop() calls for OFF/EXTERNAL modes
  since our native stop is now safe

Removed: arti-mobile-ex dependency from build.gradle and version catalog

Native libraries must be built separately:
  cd tools/arti-build && ./build-arti.sh

https://claude.ai/code/session_01BApgDd5udqBzMqysSRMpZu
2026-04-01 16:37:44 +00:00
Claude 11af50e0ad feat: migrate Tor implementation from tor-android to Arti (Rust)
Replace Guardian Project's tor-android (C Tor) and jtorctl with
arti-mobile-ex, a Rust-based Tor implementation. This eliminates
the Android Service binding complexity in favor of an in-process
ArtiProxy object.

Key changes:
- TorService: Replace ServiceConnection to org.torproject.jni.TorService
  with direct ArtiProxy.Builder/start/stop API. Bootstrap state detected
  via log parsing (following BitChat's pattern).
- TorServiceStatus: Remove TorControlConnection field (Arti doesn't
  support jtorctl control protocol).
- RelayProxyClientConnector: Remove DORMANT/ACTIVE/NEWNYM control
  signals. Arti manages its own circuit lifecycle internally.
- Dependencies: Replace tor-android + jtorctl with arti-mobile-ex 1.2.3.

TorManager's external API (status/activePortOrNull StateFlows) and all
downstream consumers (DualHttpClientManager, TorSettings, UI) are
unchanged.

https://claude.ai/code/session_01BApgDd5udqBzMqysSRMpZu
2026-03-31 22:00:48 +00:00
Vitor Pamplona ae99700cef Updates media 2026-03-29 10:02:52 -04:00
Vitor Pamplona da060a27fd Merge pull request #1970 from davotoula/update-video-compression-library
Update video compression library
2026-03-27 18:57:20 -04:00
davotoula bb469700b7 update video compression library to latest 2026-03-27 23:25:55 +01:00
Vitor Pamplona 323694761e Updating dependencies 2026-03-27 16:21:16 -04:00
Vitor Pamplona 009b5655f9 Merge branch 'main' into claude/add-negentropy-support-PcTpR 2026-03-27 08:15:24 -04:00
Vitor Pamplona 75a8e461ad 20x Faster Rfc3986Normalizer and way less objects being created. 2026-03-26 19:04:18 -04:00
Claude 70baf55e1c feat: add NIP-77 negentropy sync support
Add support for NIP-77 (Negentropy Syncing) protocol messages using
the negentropy-kmp library for efficient set reconciliation between
client and relay.

New files:
- NegOpenCmd, NegMsgCmd, NegCloseCmd: Client-to-relay commands
- NegMsgMessage, NegErrMessage: Relay-to-client messages
- NegentropySession: Client-side reconciliation orchestrator
- NegentropyServerSession: Server-side reconciliation handler
- NegentropyManager: High-level sync manager with listener callbacks

Updated serializers:
- Jackson MessageSerializer/Deserializer for NEG-MSG, NEG-ERR
- Jackson CommandSerializer/Deserializer for NEG-OPEN, NEG-MSG, NEG-CLOSE
- Kotlin MessageKSerializer/CommandKSerializer for all NIP-77 types

Compatible with strfry relay's negentropy implementation via the
negentropy-kmp library which implements the same Protocol V1 spec.

https://claude.ai/code/session_01Dc6W1G1jURAAR9kzyVvk6g
2026-03-26 04:23:32 +00:00
Vitor Pamplona d84d146981 Merge branch 'main' into claude/remove-libsodium-dependency-DjtWa 2026-03-25 11:24:55 -04:00