Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# appv2 Development Guidelines

Auto-generated from all feature plans. Last updated: 2026-03-22
Auto-generated from all feature plans. Last updated: 2026-03-23

## Active Technologies
- Rust stable 1.75+ (core logic); Dart 3.x / Flutter 3.x (UI) + nostr-sdk 0.44+, mostro-core, flutter_rust_bridge 2.x, Riverpod (state management), go_router (navigation), sqlx (SQLite), indexed_db_futures (web), bip32/bip39 (key derivation), chacha20poly1305 (file encryption) (001-mostro-p2p-client)
Expand Down
7 changes: 5 additions & 2 deletions specs/001-mostro-p2p-client/contracts/orders.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ Publish a new order to the Mostro network.
```text
NewOrderParams {
kind: OrderKind # Buy or Sell
fiat_amount: f64 # Amount in fiat
fiat_amount: f64? # Fixed amount in fiat (null if range)
fiat_amount_min: f64? # Min amount for range orders (null if fixed)
fiat_amount_max: f64? # Max amount for range orders (null if fixed)
fiat_code: String # ISO 4217 code
payment_method: String # Payment method description
premium: f64 # Price premium/discount %
Expand All @@ -46,7 +48,8 @@ NewOrderParams {
```

**Validation**:
- `fiat_amount` MUST be > 0
- Either `fiat_amount` OR both `fiat_amount_min` and `fiat_amount_max` MUST be provided (not both)
- If range: `fiat_amount_min` MUST be > 0 and < `fiat_amount_max`
- `fiat_code` MUST be valid ISO 4217
- `payment_method` MUST not be empty

Expand Down
44 changes: 39 additions & 5 deletions specs/001-mostro-p2p-client/contracts/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ Buy | Sell
### OrderStatus
```text
Pending | WaitingBuyerInvoice | WaitingPayment | Active | FiatSent
| SettledHoldInvoice | Success | Canceled | Expired
| CooperativelyCanceled | CanceledByAdmin | SettledByAdmin | Dispute
| SettledHoldInvoice | Success | PaymentFailed | Canceled | Expired
| CooperativelyCanceled | CanceledByAdmin | SettledByAdmin
| CompletedByAdmin | Dispute
```

### TradeRole
Expand All @@ -43,7 +44,7 @@ Buyer(BuyerStep) | Seller(SellerStep) | Disputed

### TradeOutcome
```text
Success | Canceled | Expired | DisputeWon | DisputeLost
Success | PaymentFailed | Canceled | Expired | DisputeWon | DisputeLost
```

### MessageType
Expand Down Expand Up @@ -96,6 +97,16 @@ Pending | Downloading | Downloaded | Failed
Connected | Disconnected | Connecting | Error
```

### ThemeMode
```text
System | Dark | Light
```

### LogLevel
```text
Debug | Info | Warning | Error
```

## Structs (Dart-visible)

### OrderInfo
Expand All @@ -104,7 +115,9 @@ id: String
kind: OrderKind
status: OrderStatus
amount_sats: u64?
fiat_amount: f64
fiat_amount: f64?
fiat_amount_min: f64?
fiat_amount_max: f64?
Comment thread
coderabbitai[bot] marked this conversation as resolved.
fiat_code: String
payment_method: String
premium: f64
Expand Down Expand Up @@ -155,11 +168,18 @@ download_status: DownloadStatus
local_path: String?
```

### RelaySource
```text
Default | MostroDiscovered | UserAdded
```

### RelayInfo
```text
url: String
is_active: bool
is_default: bool
source: RelaySource
is_blacklisted: bool
status: RelayStatus
last_connected_at: i64?
last_error: String?
Expand All @@ -169,7 +189,9 @@ last_error: String?
```text
id: String
order_kind: OrderKind
fiat_amount: f64
fiat_amount: f64?
fiat_amount_min: f64?
fiat_amount_max: f64?
fiat_code: String
amount_sats: u64?
payment_method: String
Expand All @@ -187,6 +209,15 @@ trade_key_index: u32
created_at: i64
```

### LogEntry
```text
id: u32
level: LogLevel
tag: String
message: String
timestamp: i64
```

### AppState
```text
connection: ConnectionState
Expand All @@ -195,4 +226,7 @@ has_active_trade: bool
has_nwc_wallet: bool
unread_messages: u32
pending_queue_count: u32
theme: ThemeMode
privacy_mode: bool
logging_enabled: bool
```
27 changes: 18 additions & 9 deletions specs/001-mostro-p2p-client/data-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Represents the user's cryptographic identity. One per app installation.
| created_at | Timestamp | When identity was created |
| last_used_at | Timestamp | Last activity timestamp |
| trade_key_index | u32 | Current BIP-32 trade key index (N in m/44'/1237'/38383'/0/N) |
| privacy_mode | bool | Whether user is in privacy mode (no reputation) |
| privacy_mode | bool | Whether user is in privacy mode (no reputation). **Authoritative source.** The Settings `privacy_mode` key is a convenience alias: writes MUST go through `set_privacy_mode()` on the Identity API, which updates Identity first and then propagates to Settings. On read, Identity.privacy_mode wins any conflict. Settings MUST NOT be written directly for this key. |
| derivation_path | String | BIP-32 base path: `m/44'/1237'/38383'/0` |

**Validation rules**:
Expand All @@ -39,7 +39,9 @@ A buy or sell offer on the Mostro network.
| kind | Enum | `Buy` or `Sell` |
| status | Enum | See state machine below |
| amount_sats | u64? | Amount in satoshis (null if fiat-defined) |
| fiat_amount | f64 | Amount in fiat currency |
| fiat_amount | f64? | Fixed amount in fiat (null if range order) |
| fiat_amount_min | f64? | Min fiat amount for range orders (null if fixed) |
| fiat_amount_max | f64? | Max fiat amount for range orders (null if fixed) |
| fiat_code | String | ISO 4217 currency code (e.g., "USD", "EUR") |
| payment_method | String | Fiat payment method description |
| premium | f64 | Price premium/discount percentage |
Expand All @@ -52,22 +54,25 @@ A buy or sell offer on the Mostro network.

**Validation rules**:
- `fiat_code` MUST be a valid ISO 4217 code.
- `amount_sats` or `fiat_amount` MUST be > 0.
- Either `fiat_amount` OR both `fiat_amount_min` and `fiat_amount_max` MUST be provided, but NOT both. If `fiat_amount` is present, `fiat_amount_min` and `fiat_amount_max` MUST be absent; if `fiat_amount_min`/`fiat_amount_max` are present, `fiat_amount` MUST be absent.
- If range: `fiat_amount_min` MUST be > 0 and < `fiat_amount_max`.
- `premium` is a signed float (negative = discount).

**State machine**:
```
**State machine** (15 states):
```text
Pending
├─→ WaitingBuyerInvoice
│ └─→ WaitingPayment
│ ├─→ Active
│ │ ├─→ FiatSent
│ │ │ └─→ SettledHoldInvoice → Success
│ │ │ → PaymentFailed (buyer resubmits invoice)
│ │ └─→ Dispute
│ │ ├─→ CanceledByAdmin
│ │ └─→ SettledByAdmin
│ └─→ Expired
├─→ Canceled (by creator)
│ │ ├─→ SettledByAdmin
│ │ └─→ CompletedByAdmin
│ └─→ Expired (protocol-enforced inactivity timeout, e.g., buyer never paid within deadline)
├─→ Canceled (explicit user action: creator cancels own untaken order)
└─→ CooperativelyCanceled
```

Expand Down Expand Up @@ -144,6 +149,8 @@ A Nostr relay the app connects to.
| url | String | WebSocket URL (wss://...) |
| is_active | bool | Whether app should connect to this relay |
| is_default | bool | Whether this is a preconfigured default |
| source | Enum | `Default`, `MostroDiscovered`, `UserAdded` |
| is_blacklisted | bool | If true, relay will not be auto-added even if daemon announces it |
| status | Enum | `Connected`, `Disconnected`, `Connecting`, `Error` |
| last_connected_at | Timestamp? | Last successful connection |
| last_error | String? | Most recent error message |
Expand Down Expand Up @@ -189,7 +196,9 @@ User preferences stored locally.

**Known keys**: `theme` (dark/light/system), `locale` (language code),
`pin_enabled` (bool), `biometric_enabled` (bool),
`default_fiat_currency` (ISO code), `notification_enabled` (bool).
`default_fiat_currency` (ISO code), `notification_enabled` (bool),
`privacy_mode` (bool — global toggle, applies to future trades),
`logging_enabled` (bool — diagnostic logging, runtime-only: not persisted to storage; startup code unconditionally sets this to `false` on process start regardless of any prior value).

---

Expand Down
4 changes: 2 additions & 2 deletions specs/001-mostro-p2p-client/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Build a multi-platform (iOS, Android, Web, macOS, Windows, Linux) P2P Bitcoin/Li
**Project Type**: Multi-platform mobile/desktop/web app
**Performance Goals**: App launch + order list < 2s; message delivery < 1s; 60 fps UI; queued messages sent < 5s after reconnection
**Constraints**: Offline-capable, zero plaintext key storage, no analytics/telemetry, no network calls from Dart, all crypto in Rust only
**Scale/Scope**: ~15 screens, one active trade at a time, single user identity per installation
**Scale/Scope**: ~15 screens, one active trade at a time, single user identity per installation, 15 protocol-defined order states

## Constitution Check

Expand All @@ -40,7 +40,7 @@ Build a multi-platform (iOS, Android, Web, macOS, Windows, Linux) P2P Bitcoin/Li
|-----------|--------|-------|
| I. Rust Core, Flutter Shell | **PASS** | All contracts define Rust-side APIs exposed via flutter_rust_bridge. No Dart crypto or network calls in any contract. |
| II. Privacy by Design | **PASS** | File attachments use ChaCha20-Poly1305 in Rust. Push notifications carry zero content. NWC credentials encrypted at rest. |
| III. Protocol Compliance | **PASS** | Order state machine matches Mostro protocol exactly. All actions (TakeSell, FiatSent, Release, Dispute, etc.) mapped in contracts. |
| III. Protocol Compliance | **PASS** | Order state machine matches Mostro protocol exactly (15 states including PaymentFailed, CompletedByAdmin). All actions mapped in contracts. |
| IV. Offline-First Architecture | **PASS** | MessageQueue contract handles offline outbox. Orders cached locally with `cached_at` timestamp. |
| V. Multi-Platform from Day One | **PASS** | Storage trait with SQLite/IndexedDB backends. Async runtime feature-gated for WASM vs native. |
| VI. Simplicity Over Features | **PASS** | Contracts expose focused APIs per domain. No multipurpose interfaces. |
Expand Down
7 changes: 4 additions & 3 deletions specs/001-mostro-p2p-client/research.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ dependency ensures type-level compatibility with any conforming daemon.
4. The `p` tag on Gift Wrap points to recipient; sender identity is hidden.
5. Relays only see ephemeral keys, not real participants.

**Order state machine**:
**Order state machine** (15 states):
```text
Pending
→ WaitingBuyerInvoice (buy order taken, awaiting buyer invoice)
Expand All @@ -115,10 +115,11 @@ Pending
→ FiatSent (buyer marked fiat sent)
→ SettledHoldInvoice (seller confirmed, funds released)
→ Success (trade complete)
→ PaymentFailed (LN payment to buyer failed, buyer may resubmit invoice)
→ Dispute (either party disputes)
→ CanceledByAdmin | SettledByAdmin
→ CanceledByAdmin | SettledByAdmin | CompletedByAdmin
→ Expired (buyer never paid, timeout)
→ Canceled (creator canceled before taker)
→ Canceled (creator canceled or timeout)
→ CooperativelyCanceled (both parties agreed)
```

Expand Down
Loading