Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 38 additions & 37 deletions .github/workflows/gradle-publish.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created
# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is published
# For more info see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle

name: Gradle Publish
name: Gradle Package

on:
release:
types: [created]
types: [published]
workflow_dispatch:
inputs:
version:
description: 'Version to publish (e.g. v0.1.0)'
description: "Version to publish (e.g., v0.1.0)"
required: true

jobs:
Expand All @@ -18,37 +18,38 @@ jobs:
permissions:
contents: read
packages: write
defaults:
run:
working-directory: bindings/android

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
server-id: github
settings-path: ${{ github.workspace }}

- name: Extract version
id: version
shell: bash
run: |
VERSION="${{ inputs.version }}"
if [[ -z "$VERSION" ]]; then
VERSION="$GITHUB_REF_NAME"
fi
echo "version=${VERSION#v}" >> $GITHUB_OUTPUT

- name: Build
run: ./gradlew assembleRelease -Pversion=${{ steps.version.outputs.version }}

- name: Publish
run: ./gradlew publish -Pversion=${{ steps.version.outputs.version }}
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.ORG_PACKAGES_TOKEN }}
GITHUB_REPO: ${{ github.repository }}
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
server-id: github
settings-path: ${{ github.workspace }}

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Extract version from input or tag
id: version
shell: bash
run: |
VERSION="${{ inputs.version }}"
if [[ -z "$VERSION" ]]; then
VERSION="$GITHUB_REF_NAME"
fi
echo "version=${VERSION#v}" >> $GITHUB_OUTPUT

- name: Build with Gradle
working-directory: bindings/android
run: ./gradlew build -Pversion=${{ steps.version.outputs.version }}

- name: Publish to GitHub Packages
working-directory: bindings/android
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.ORG_PACKAGES_TOKEN }}
GITHUB_REPO: ${{ github.repository }}
run: ./gradlew publish -Pversion=${{ steps.version.outputs.version }}
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
/httpRequests/
.ai
.build
81 changes: 81 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Cross-platform FFI library wrapping the [VSS (Versioned Storage Service) Rust Client](https://github.com/lightningdevkit/vss-server) via UniFFI. Generates bindings for Swift (iOS), Kotlin (Android), and Python. Used by mobile Lightning wallets to back up both app data and LDK (Lightning Development Kit) channel state to a VSS server.

## Build & Test Commands

```bash
cargo build # Debug build
cargo build --release # Release build
cargo test # All tests
cargo test tests::tests --lib # Unit tests only
cargo test ffi_tests --lib # FFI interface tests only
cargo clippy # Lint

# Platform bindings (requires platform-specific toolchains)
./build.sh ios # XCFramework + Swift bindings
./build.sh android # JNI libs + Kotlin bindings
./build.sh python # Python package
./build.sh all # All platforms
```

## Architecture

### Dual Client Design

The library maintains two separate global singleton clients, each with its own key derivation:

- **VssClient** (`implementation.rs`) — App backup client. Uses a **truncated 32-byte** seed for key derivation (backward compatible with v0.4.0). Manages general app data storage.
- **LdkVssClient** (`ldk_client.rs`) — Dedicated LDK client. Uses the **full 64-byte** BIP39 seed, matching ldk-node's exact key derivation chain so it can correctly deobfuscate keys stored by ldk-node's VssStore.

Both clients are stored as `OnceCell<Arc<Mutex<Option<T>>>>` statics in `lib.rs`.

### Key Derivation Paths (BIP32)

- `m/877'` — VSS master derivation → 32-byte `vss_seed` → HKDF → (data_encryption_key, obfuscation_key)
- `m/877'/138'` — LNURL-auth signing key (both clients derive from truncated seed for same server identity)
- `m/877'/118'` — Store ID derivation

The critical difference: app client truncates the 64-byte BIP39 seed to 32 bytes before deriving the master key, while the LDK client uses all 64 bytes. This must be preserved for backward compatibility.

### FFI Layer (`lib.rs`)

- `execute_async!` macro bridges sync FFI calls to the single-threaded Tokio runtime
- All exported functions are annotated with `#[uniffi::export]`
- `uniffi::setup_scaffolding!()` generates the FFI glue code
- UniFFI config in `uniffi.toml` (Kotlin package: `com.synonym.vssclient`)

### LDK Namespaces (`types.rs`)

`LdkNamespace` enum maps to ldk-node's storage layout: `Default`, `Monitors`, `MonitorUpdates { monitor_id }`, `ArchivedMonitors`. Each namespace becomes a key prefix in VSS.

### Retry Policy

Exponential backoff (10ms base, 10ms jitter, max 10 attempts, 15s total). Skips retry for NoSuchKey, InvalidRequest, and Conflict errors.

## Related Repositories

When investigating VSS server behavior, protocol details, or LDK key derivation, check the sibling repos:

- **vss-server**: `../vss-server`
- **ldk-node**: `../ldk-node`

## Release Workflow (MANDATORY)

1. **ALWAYS** bump the version in `Cargo.toml` before generating bindings.
2. **ALWAYS** bump the iOS framework tag in `Package.swift` to match the new version.
3. **ALWAYS** regenerate all bindings with `./build.sh all` after any code changes.
4. **ALWAYS** upload `bindings/ios/VssRustClientFfi.xcframework.zip` to new GitHub releases.

## Commit Convention

```
feat: ... # New features
fix: ... # Bug fixes
refactor: ... # Code restructuring
chore: ... # Bindings updates, dependencies
```
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[package]
name = "vss-rust-client-ffi"
version = "0.4.0"
version = "0.5.12"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
crate-type = ["cdylib"]
crate-type = ["cdylib", "staticlib"]
name = "vss_rust_client_ffi"


Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

import PackageDescription

let tag = "v0.4.0"
let checksum = "d29d24123afb6d91ee3fdb474e7aff3eafae8a33aff199f54ec2ea4eaef705ed"
let tag = "v0.5.12"
let checksum = "e6068b6d3301af092a65a6f8f4571165b620655c88ba7e0ce3eb8c8dcc1b096e"
let url = "https://github.com/synonymdev/vss-rust-client-ffi/releases/download/\(tag)/VssRustClientFfi.xcframework.zip"

let package = Package(
Expand Down
Loading