Skip to content
Melvin Carvalho edited this page Jan 8, 2026 · 1 revision

Bitmark Marking Client - Phased Design Plan

Overview

This document outlines the transformation of Jumble (a Nostr client) into a Bitmark marking client. The Bitmark marking system combines reputation and currency into "marks" that can be awarded to users and content.

Key Concepts

  • Marks: Divisible units (to 5 decimal places) that function as both reputation and currency
  • Marking: The act of awarding marks to users or content as recognition
  • Bitmark: The underlying cryptocurrency (operational since 2014, uses 8 PoW algorithms)
  • Spendable Karma: Marks can be stored, exchanged, and transferred without value loss

Phase 1: Rebrand

Objective

Transform Jumble's visual identity into a Bitmark marking client.

1.1 Brand Assets

Asset Current New
App Name Jumble Marked (or "GetMarked")
Logo src/assets/Logo.tsx New Bitmark-inspired mark logo
Icon src/assets/Icon.tsx Stylized "M" or checkmark symbol
Favicon Jumble favicon Mark icon
OGP Images Jumble branding Marked branding

1.2 Color Palette

// src/lib/theme.ts (new)
export const markColors = {
  primary: '#FF6B35',      // Warm orange (marking action)
  secondary: '#2E4057',    // Deep blue (trust/stability)
  accent: '#48A9A6',       // Teal (growth/reputation)
  gold: '#D4AF37',         // Gold (high-value marks)
  silver: '#C0C0C0',       // Silver (moderate marks)
  bronze: '#CD7F32',       // Bronze (entry marks)
}

1.3 Files to Modify

src/assets/Logo.tsx          → New mark logo SVG
src/assets/Icon.tsx          → New icon SVG
public/favicon.ico           → New favicon
public/manifest.json         → App name, description
index.html                   → Title, meta tags, OGP
src/i18n/*/translation.json  → All "Jumble" references
vite.config.ts               → PWA manifest

1.4 Terminology Mapping

Jumble Term Marked Term
Zap Mark
Sats Marks
Lightning Bitmark
Wallet Mark Wallet
Tip Award

1.5 Deliverables

  • New logo component
  • New icon component
  • Updated manifest.json
  • Updated index.html (title, OGP)
  • Updated translations
  • Color theme implementation
  • Loading screens with new branding

Phase 2: User Wallets

Objective

Integrate Bitmark wallet functionality so users can hold and manage marks.

2.1 Architecture

┌─────────────────────────────────────────────────────────┐
│                    MarkProvider                          │
├─────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐ │
│  │ KeyManager  │  │ BalanceSync │  │ TransactionLog  │ │
│  └─────────────┘  └─────────────┘  └─────────────────┘ │
├─────────────────────────────────────────────────────────┤
│                  BitmarkRPC Service                      │
│              (connects to bitmarkd node)                 │
└─────────────────────────────────────────────────────────┘

2.2 Wallet Options

Option A: HD Wallet Derivation from Nostr Keys

// Derive Bitmark address from Nostr private key
// BIP-32 style derivation: m/44'/19'/0'/0/0
// Where 19 is a hypothetical Bitmark coin type

interface MarkWallet {
  address: string           // Bitmark address
  publicKey: string         // Derived from nostr key
  balance: number           // Current mark balance
  pending: number           // Unconfirmed marks
}

Option B: Separate Wallet Import

interface ImportedWallet {
  type: 'wif' | 'seed' | 'watch-only'
  address: string
  encryptedPrivKey?: string  // Encrypted with nostr key
}

Option C: Custodial Marks (Recommended for MVP)

// Server-managed marks linked to nostr pubkey
interface CustodialMark {
  pubkey: string           // Nostr pubkey as identifier
  balance: number          // Marks held in custody
  pendingIn: number        // Incoming marks
  pendingOut: number       // Outgoing marks
}

2.3 New Provider

// src/providers/MarkProvider.tsx
type TMarkContext = {
  isWalletConnected: boolean
  address: string | null
  balance: number
  pendingBalance: number
  defaultMarkAmount: number
  updateDefaultAmount: (amount: number) => void
  quickMark: boolean
  updateQuickMark: (enabled: boolean) => void
  sendMark: (recipient: string, amount: number, memo?: string) => Promise<TxResult>
  getHistory: () => Promise<MarkTransaction[]>
}

2.4 New Components

src/components/MarkWallet/
├── index.tsx              # Main wallet component
├── BalanceDisplay.tsx     # Shows mark balance
├── ReceiveMarks.tsx       # QR code / address display
├── SendMarks.tsx          # Send marks form
├── TransactionHistory.tsx # Mark transaction list
└── WalletSetup.tsx        # Initial wallet setup flow

2.5 Storage Schema

// Local storage keys
const MARK_STORAGE = {
  WALLET_ADDRESS: 'mark:wallet:address',
  ENCRYPTED_KEY: 'mark:wallet:encKey',
  DEFAULT_AMOUNT: 'mark:default:amount',  // Default: 1.0 marks
  QUICK_MARK: 'mark:quick:enabled',
  TX_CACHE: 'mark:tx:cache'
}

2.6 API Integration Points

// src/services/bitmark.service.ts
interface BitmarkService {
  // Balance queries
  getBalance(address: string): Promise<number>
  getUnconfirmed(address: string): Promise<number>

  // Transaction operations
  createTransaction(params: TxParams): Promise<RawTx>
  broadcastTransaction(signedTx: string): Promise<TxId>
  getTransaction(txId: string): Promise<Transaction>

  // Address utilities
  validateAddress(address: string): boolean
  deriveAddress(pubkey: string): string
}

2.7 Deliverables

  • MarkProvider context
  • Wallet creation/import flow
  • Balance display component
  • Receive marks (address + QR)
  • Basic send functionality
  • Transaction history view
  • Bitmark RPC service
  • Secure key storage

Phase 3: Mark Users

Objective

Allow users to award marks to other users as reputation.

3.1 User Flow

┌──────────┐     ┌───────────────┐     ┌──────────────┐
│ Profile  │ ──► │ Mark Button   │ ──► │ Mark Dialog  │
└──────────┘     └───────────────┘     └──────────────┘
                                              │
                 ┌───────────────┐            ▼
                 │ Confirmation  │ ◄── ┌──────────────┐
                 └───────────────┘     │ Amount Input │
                        │              └──────────────┘
                        ▼
                 ┌───────────────┐
                 │ Transaction   │
                 │ Broadcast     │
                 └───────────────┘

3.2 Profile Integration

// Modify: src/components/Profile/index.tsx
// Replace ProfileZapButton with ProfileMarkButton

<ProfileMarkButton
  pubkey={pubkey}
  onMark={(amount) => handleMark(amount)}
/>

3.3 Mark Button Component

// src/components/ProfileMarkButton/index.tsx
interface MarkButtonProps {
  pubkey: string
  size?: 'sm' | 'md' | 'lg'
  showAmount?: boolean
}

// Displays a mark icon with optional quick-mark
// Long press opens amount selection dialog

3.4 Mark Dialog

// src/components/MarkDialog/index.tsx
interface MarkDialogProps {
  recipientPubkey: string
  recipientProfile?: Profile
  onSubmit: (amount: number, memo?: string) => Promise<void>
  onCancel: () => void
}

// Features:
// - Preset amounts: 0.1, 1.0, 5.0, 10.0 marks
// - Custom amount input
// - Optional memo/reason
// - Balance check before submit
// - Transaction fee display

3.5 Reputation Display

// src/components/ReputationBadge/index.tsx
interface ReputationBadgeProps {
  pubkey: string
  size?: 'sm' | 'md'
}

// Shows:
// - Total marks received
// - Mark rank tier (Bronze/Silver/Gold/Platinum)
// - Trend indicator (up/down/stable)

3.6 Nostr Event Structure (Optional)

// Kind for mark receipts (custom Nostr event)
const MARK_RECEIPT_KIND = 9735  // Similar to zap receipt

interface MarkReceipt {
  kind: 9735
  pubkey: string  // Marker's pubkey
  tags: [
    ['p', recipientPubkey],
    ['amount', '1.00000'],  // Mark amount
    ['txid', 'bitmark_tx_id'],
    ['memo', 'Great content!']
  ]
  content: string  // Optional message
  sig: string
}

3.7 Deliverables

  • ProfileMarkButton component
  • MarkDialog component
  • ReputationBadge component
  • Mark transaction flow
  • Replace ZapButton with MarkButton
  • Quick-mark functionality
  • Mark receipt events (Nostr)
  • User reputation aggregation

Phase 4: Mark Posts

Objective

Enable marking of individual posts/content, not just users.

4.1 Content Marking Flow

┌──────────────┐     ┌─────────────┐     ┌───────────────┐
│ Note/Post    │ ──► │ Mark Icon   │ ──► │ Mark Dialog   │
│ (in feed)    │     │ (footer)    │     │ (for content) │
└──────────────┘     └─────────────┘     └───────────────┘
                                                │
                          ┌─────────────────────┘
                          ▼
                   ┌──────────────┐
                   │ Mark stored  │
                   │ with eventId │
                   └──────────────┘

4.2 Note Footer Integration

// Modify: src/components/NoteStats/index.tsx
// Add MarkStats alongside likes, reposts, replies

<div className="flex items-center gap-4">
  <LikeButton eventId={eventId} />
  <RepostButton eventId={eventId} />
  <ReplyButton eventId={eventId} />
  <MarkButton eventId={eventId} authorPubkey={pubkey} />  // NEW
</div>

4.3 Content Mark Button

// src/components/ContentMarkButton/index.tsx
interface ContentMarkButtonProps {
  eventId: string          // Nostr event ID
  authorPubkey: string     // Content author
  initialMarks?: number    // Cached mark count
}

// Shows:
// - Mark icon (filled if user has marked)
// - Total marks received by this content
// - Click to mark, long-press for amount

4.4 Mark Aggregation

// src/hooks/useContentMarks.ts
interface ContentMarks {
  eventId: string
  totalMarks: number
  markCount: number        // Number of unique markers
  topMarkers: Marker[]     // Top 3 markers
  userMarked: boolean      // Has current user marked
  userMarkAmount?: number  // User's mark amount
}

function useContentMarks(eventId: string): ContentMarks

4.5 Leaderboards

// src/components/MarkLeaderboard/index.tsx
interface LeaderboardProps {
  type: 'users' | 'content' | 'markers'
  timeframe: 'day' | 'week' | 'month' | 'all'
  limit?: number
}

// Shows ranked list of:
// - Most marked users
// - Most marked content
// - Most generous markers

4.6 Content Mark Receipt

// Nostr event for content marks
interface ContentMarkReceipt {
  kind: 9735
  tags: [
    ['e', eventId],           // Marked content
    ['p', authorPubkey],      // Content author (recipient)
    ['amount', '0.50000'],
    ['txid', 'bitmark_tx_id']
  ]
  content: 'Excellent article!'
}

4.7 Deliverables

  • ContentMarkButton component
  • Mark stats in note footer
  • useContentMarks hook
  • Mark aggregation service
  • Leaderboard component
  • Content mark receipts
  • "Marked" indicator on posts
  • Sort feed by marks option

Phase 5: Store Marked Content

Objective

Persist marked content for discovery, analytics, and proof.

5.1 Storage Architecture

┌─────────────────────────────────────────────────────────────┐
│                      Client Layer                            │
├─────────────────────────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐  │
│  │ LocalStorage │  │ IndexedDB    │  │ Nostr Relays     │  │
│  │ (cache)      │  │ (full store) │  │ (mark receipts)  │  │
│  └──────────────┘  └──────────────┘  └──────────────────┘  │
├─────────────────────────────────────────────────────────────┤
│                     Mark Index Service                       │
│            (aggregates marks across sources)                 │
└─────────────────────────────────────────────────────────────┘

5.2 IndexedDB Schema

// src/services/mark-db.service.ts
interface MarkDB {
  marks: {
    id: string              // txid
    type: 'user' | 'content'
    fromPubkey: string
    toPubkey: string
    eventId?: string        // For content marks
    amount: number
    memo?: string
    timestamp: number
    blockHeight?: number
    confirmed: boolean
  }

  aggregates: {
    id: string              // pubkey or eventId
    type: 'user' | 'content'
    totalReceived: number
    totalSent: number
    markCount: number
    lastUpdated: number
  }

  content: {
    eventId: string
    content: string         // Cached content
    authorPubkey: string
    totalMarks: number
    createdAt: number
    cachedAt: number
  }
}

5.3 Sync Service

// src/services/mark-sync.service.ts
interface MarkSyncService {
  // Sync mark receipts from Nostr relays
  syncMarkReceipts(since: number): Promise<void>

  // Sync blockchain confirmations
  syncConfirmations(): Promise<void>

  // Subscribe to real-time marks
  subscribeToMarks(pubkey: string): () => void

  // Export data
  exportMarks(format: 'json' | 'csv'): Promise<Blob>
}

5.4 Content Preservation

// When content is marked, cache it
interface CachedContent {
  eventId: string
  rawEvent: NostrEvent      // Full Nostr event
  renderedContent: string   // Processed content
  mediaUrls: string[]       // Referenced media
  markedAt: number
  firstMarkTxId: string
}

5.5 Analytics Dashboard

// src/pages/secondary/MarkAnalytics/index.tsx
interface MarkAnalytics {
  // User stats
  totalReceived: number
  totalSent: number
  netMarks: number

  // Trends
  dailyReceived: number[]
  dailySent: number[]

  // Top content
  topMarkedContent: ContentMark[]

  // Top connections
  topReceivers: UserMark[]
  topGivers: UserMark[]
}

5.6 Deliverables

  • IndexedDB service for marks
  • Mark sync service
  • Content caching on mark
  • Analytics dashboard
  • Export functionality
  • Real-time subscriptions
  • Offline mark queue

Phase 6: Anchored Data Structures

Objective

Decentralize marked data storage using cryptographic anchoring.

6.1 Anchor Architecture

┌──────────────────────────────────────────────────────────────────┐
│                         Anchor Layer                              │
├──────────────────────────────────────────────────────────────────┤
│                                                                   │
│   ┌─────────────┐     ┌─────────────┐     ┌─────────────────┐   │
│   │ Merkle Tree │ ──► │ Tree Root   │ ──► │ Bitmark Anchor  │   │
│   │ (marks)     │     │ (hash)      │     │ (OP_RETURN)     │   │
│   └─────────────┘     └─────────────┘     └─────────────────┘   │
│                                                                   │
├──────────────────────────────────────────────────────────────────┤
│                      Distributed Storage                          │
│                                                                   │
│   ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐    │
│   │  IPFS    │   │  Nostr   │   │  Arweave │   │  Local   │    │
│   │  Nodes   │   │  Relays  │   │  (opt)   │   │  Peers   │    │
│   └──────────┘   └──────────┘   └──────────┘   └──────────┘    │
│                                                                   │
└──────────────────────────────────────────────────────────────────┘

6.2 Merkle Tree Structure

// src/lib/merkle-anchor.ts
interface MarkMerkleTree {
  // Each leaf is a mark receipt
  leaves: MarkLeaf[]

  // Tree structure
  root: string              // SHA-256 root hash
  depth: number

  // Proof generation
  getProof(markId: string): MerkleProof
  verifyProof(proof: MerkleProof): boolean
}

interface MarkLeaf {
  markId: string            // txid
  fromPubkey: string
  toPubkey: string
  eventId?: string
  amount: number
  timestamp: number
  hash: string              // leaf hash
}

interface MerkleProof {
  leaf: MarkLeaf
  siblings: string[]        // Sibling hashes
  path: ('left' | 'right')[]
  root: string
}

6.3 Bitmark Anchoring

// src/services/anchor.service.ts
interface AnchorService {
  // Create anchor transaction
  createAnchor(merkleRoot: string): Promise<AnchorTx>

  // Verify anchor
  verifyAnchor(txid: string, expectedRoot: string): Promise<boolean>

  // Get anchor history
  getAnchors(since: number): Promise<Anchor[]>
}

interface Anchor {
  txid: string              // Bitmark transaction ID
  merkleRoot: string        // Anchored root
  blockHeight: number
  timestamp: number
  markCount: number         // Number of marks in tree
}

6.4 Distributed Storage Layer

// src/services/distributed-storage.ts
interface DistributedStorage {
  // Store mark tree
  store(tree: MarkMerkleTree): Promise<StorageReceipt>

  // Retrieve and verify
  retrieve(root: string): Promise<MarkMerkleTree>

  // Pin important content
  pin(cid: string): Promise<void>

  // Replicate across nodes
  replicate(cid: string, nodes: string[]): Promise<void>
}

interface StorageReceipt {
  cid: string               // IPFS CID
  nostrEventId: string      // Backup on Nostr
  replicaCount: number
  anchorTxId?: string       // If anchored
}

6.5 IPFS Integration

// src/services/ipfs.service.ts
interface IPFSService {
  // Add mark tree
  addTree(tree: MarkMerkleTree): Promise<string>  // Returns CID

  // Get tree by CID
  getTree(cid: string): Promise<MarkMerkleTree>

  // Pin locally
  pin(cid: string): Promise<void>

  // Connect to peers
  connectPeer(peerId: string): Promise<void>
}

6.6 Nostr as Backup Layer

// Use Nostr relays as distributed backup
const MARK_TREE_KIND = 30078  // Replaceable event for tree

interface MarkTreeEvent {
  kind: 30078
  tags: [
    ['d', merkleRoot],        // Unique identifier
    ['anchor', txid],          // Bitmark anchor
    ['cid', ipfsCid],          // IPFS location
    ['count', '1234'],         // Mark count
  ]
  content: JSON.stringify(compressedTree)
  pubkey: aggregatorPubkey
}

6.7 Verification Flow

┌─────────────┐     ┌─────────────┐     ┌─────────────────┐
│ User wants  │ ──► │ Fetch proof │ ──► │ Verify against  │
│ to verify   │     │ from IPFS   │     │ blockchain      │
└─────────────┘     └─────────────┘     └─────────────────┘
                                               │
                    ┌──────────────────────────┘
                    ▼
             ┌─────────────────┐
             │ Proof valid!    │
             │ Mark is real    │
             └─────────────────┘

6.8 Federated Aggregators

// Multiple aggregators can create trees
interface Aggregator {
  pubkey: string
  endpoint: string
  reputation: number
  lastAnchor: number

  // Submit marks to aggregator
  submit(marks: Mark[]): Promise<void>

  // Get current tree
  getCurrentTree(): Promise<MarkMerkleTree>

  // Get proof for specific mark
  getProof(markId: string): Promise<MerkleProof>
}

6.9 Deliverables

  • Merkle tree library
  • Anchor service (Bitmark OP_RETURN)
  • IPFS integration
  • Nostr backup storage
  • Proof generation/verification
  • Aggregator protocol
  • Verification UI
  • Peer discovery
  • Cross-aggregator sync

Implementation Timeline

Phase Complexity Dependencies
1. Rebrand Low None
2. Wallets High Phase 1, Bitmark node access
3. Mark Users Medium Phase 2
4. Mark Posts Medium Phase 3
5. Store Content Medium Phase 4
6. Anchored Data High Phase 5, IPFS infrastructure

Technical Requirements

Infrastructure

  • Bitmark full node (or light client)
  • IPFS node (for Phase 6)
  • Nostr relays (existing)

Libraries

  • @noble/secp256k1 - Key derivation
  • merkletreejs - Merkle tree operations
  • ipfs-http-client - IPFS integration
  • idb - IndexedDB wrapper

APIs

  • Bitmark RPC (JSON-RPC 1.0)
  • IPFS HTTP Gateway
  • Nostr WebSocket

Security Considerations

  1. Key Management: Never store unencrypted private keys
  2. Transaction Signing: Sign transactions client-side only
  3. Proof Verification: Always verify Merkle proofs against blockchain
  4. Rate Limiting: Prevent mark spam with cooldowns
  5. Amount Limits: Set sensible min/max mark amounts

Open Questions

  1. Custodial vs non-custodial for MVP?
  2. Which Bitmark node to use as default?
  3. Aggregator incentive model?
  4. Mark-to-Nostr-Zap bridge?
  5. Mobile wallet integration (WalletConnect)?

Document Version: 1.0 Created: 2026-01-08

Clone this wiki locally