archy/docs/marketplace-protocol.md
Dorian 6fee6befed refactor: update dependencies and remove unused code
- Added new dependencies: `adler2`, `crc32fast`, `flate2`, `miniz_oxide`, and `libredox`.
- Updated existing dependencies: `tokio-rustls` to version 0.26.4 and `filetime` to version 0.2.27.
- Removed the `backup.rs` file as it is no longer needed.
- Introduced tests for configuration and credential management.
- Enhanced the `identity` module to generate W3C compliant DID documents.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 00:19:30 +00:00

12 KiB

Decentralized App Marketplace Protocol

Overview

Archipelago's community marketplace enables developers to publish app manifests to Nostr relays, where nodes discover and install them without a central app store. Trust is established through DID-signed manifests and community reputation.

Architecture

Developer Node                    Nostr Relays                    User Node
     │                                │                               │
     │── Publish signed manifest ──►  │                               │
     │   (NIP-78, kind 30078)         │                               │
     │                                │  ◄── Query app manifests ──   │
     │                                │      (filter by d-tag)        │
     │                                │                               │
     │                                │── Return signed manifests ──► │
     │                                │                               │
     │                                │      [Verify DID signature]   │
     │                                │      [Check trust score]      │
     │                                │      [Display in marketplace] │
     │                                │                               │
     │                                │      [User clicks Install]    │
     │                                │      [Pull container image]   │
     │                                │      [Start container]        │

Manifest Schema

App manifests published to Nostr relays follow the existing apps/{app-id}/manifest.yml schema (see docs/app-manifest-spec.md), serialized as JSON within a Nostr event.

Marketplace Manifest Fields

{
  "app_id": "my-bitcoin-tool",
  "name": "My Bitcoin Tool",
  "version": "1.2.0",
  "description": {
    "short": "A useful Bitcoin utility",
    "long": "Detailed description of what this app does..."
  },
  "author": {
    "name": "Developer Name",
    "did": "did:key:z6Mkh...",
    "nostr_pubkey": "npub1..."
  },
  "container": {
    "image": "docker.io/developer/my-bitcoin-tool:1.2.0",
    "ports": [{ "container": 8080, "host": 8180, "protocol": "tcp" }],
    "volumes": [{ "name": "data", "path": "/data" }],
    "env": {
      "NETWORK": "mainnet"
    },
    "capabilities": [],
    "readonly_root": true,
    "no_new_privileges": true,
    "run_as_user": 1000
  },
  "category": "money",
  "icon_url": "https://example.com/icon.png",
  "repo_url": "https://github.com/developer/my-bitcoin-tool",
  "license": "MIT",
  "min_archipelago_version": "0.1.0",
  "dependencies": [],
  "signatures": {
    "manifest_hash": "sha256:abc123...",
    "did_signature": "base64-encoded-signature"
  }
}

Required Fields

Field Type Description
app_id string Unique identifier, lowercase kebab-case
name string Human-readable display name
version string Semantic version (major.minor.patch)
description.short string One-line description (max 120 chars)
author.did string Developer's DID (did:key method)
container.image string Full container image reference with tag (never latest)
category string One of: money, commerce, data, networking, home, community, other

Security-Required Fields

Field Default Description
container.readonly_root true Container root filesystem is read-only
container.no_new_privileges true Prevent privilege escalation
container.run_as_user 1000 UID to run as (must be > 1000)
container.capabilities [] Required Linux capabilities (drop all, add only needed)

Nostr Event Format

Event Kind

App manifests use NIP-78 application-specific data with event kind 30078 (replaceable parameterized). This matches the existing node discovery pattern in nostr_discovery.rs.

Event Structure

{
  "kind": 30078,
  "tags": [
    ["d", "archipelago-app:<app_id>"],
    ["t", "archipelago-marketplace"],
    ["t", "category:<category>"],
    ["version", "<semver>"],
    ["image", "<container_image>"],
    ["L", "archipelago"],
    ["l", "app-manifest", "archipelago"]
  ],
  "content": "<JSON-serialized manifest>",
  "created_at": 1710000000,
  "pubkey": "<developer's secp256k1 pubkey hex>",
  "sig": "<schnorr signature>"
}

Tag Semantics

Tag Purpose
d Unique identifier for NIP-33 replaceable events. Format: archipelago-app:<app_id>
t Searchable topic tags for relay filtering
version Allows version-specific queries
image Container image for quick display without parsing content
L/l NIP-32 labeling namespace for structured queries

Publishing a Manifest

  1. Developer creates/updates their app manifest
  2. Serialize manifest as JSON
  3. Compute SHA-256 hash of the serialized manifest
  4. Sign the hash with the developer's DID key
  5. Embed manifest + signature in Nostr event content
  6. Sign the Nostr event with the node's secp256k1 key
  7. Publish to all configured Nostr relays

Discovering Manifests

  1. Node queries configured relays with filter:
    {
      "kinds": [30078],
      "limit": 100,
      "#t": ["archipelago-marketplace"]
    }
    
  2. For each returned event: a. Verify Nostr event signature (standard NIP-01) b. Parse manifest JSON from content c. Verify DID signature on manifest hash d. Check manifest against security requirements e. Calculate trust score
  3. Return manifests sorted by trust score

Trust Model

Trust Score Calculation

Each discovered app receives a trust score (0-100) based on:

Factor Weight Description
DID Verification 30 Manifest is signed by a valid DID key
Relay Consensus 20 Manifest found on multiple independent relays
Federation Trust 20 Developer's DID is in the user's federation network
Version History 15 App has multiple published versions (shows maintenance)
Security Compliance 15 Manifest follows all security requirements

Trust Tiers

Score Tier UI Treatment
80-100 Verified Green badge, install with one click
50-79 Community Yellow badge, install with confirmation
20-49 Unverified Orange badge, install with warning dialog
0-19 Untrusted Red badge, requires explicit security override

Federation-Based Trust

When a developer's DID appears in the user's federation network (trusted peer), the app automatically receives +20 trust points. This creates organic trust propagation: if you trust a node operator, you're more likely to trust their published apps.

ADR: Nostr Relays over Centralized Registry

Decision: Use Nostr relays as the app discovery layer instead of a centralized registry.

Context: A centralized app store contradicts Archipelago's sovereignty principles. Nostr relays provide censorship-resistant, decentralized event distribution.

Consequences:

  • (+) No single point of failure for app discovery
  • (+) Developers publish without permission or review gates
  • (+) Multiple relay sources increase availability
  • (+) Leverages existing Nostr infrastructure and key management
  • (-) No global content moderation (each node decides trust locally)
  • (-) Spam is possible (mitigated by DID verification and trust scoring)
  • (-) Relay availability varies (mitigated by querying multiple relays)

Signing Protocol

Manifest Signing (DID Layer)

1. Serialize manifest to canonical JSON (sorted keys, no whitespace)
2. Compute: manifest_hash = SHA-256(canonical_json)
3. Sign: did_signature = Ed25519_Sign(did_private_key, manifest_hash)
4. Attach to manifest:
   {
     "signatures": {
       "manifest_hash": "sha256:<hex>",
       "did_signature": "<base64>"
     }
   }

Event Signing (Nostr Layer)

Standard NIP-01 Schnorr signature over the event ID (hash of serialized event fields). This is handled by the Nostr client library.

Verification Flow

Receiving Node:
  1. Verify Nostr event signature (NIP-01)       → Proves event authenticity
  2. Extract manifest JSON from event content
  3. Compute SHA-256 of manifest content
  4. Compare with manifest.signatures.manifest_hash → Proves content integrity
  5. Resolve DID document for manifest.author.did
  6. Verify did_signature with DID public key       → Proves developer identity
  7. Check container.image tag is pinned (not :latest)
  8. Validate security fields meet minimums

RPC Endpoints

Marketplace Discovery

Method Description Auth
marketplace.discover Query relays for app manifests, verify, score, return sorted Local
marketplace.publish Publish an app manifest to configured relays Local
marketplace.get-manifest Get full manifest for a specific app by ID Local
marketplace.verify Verify a manifest's signatures and security compliance Local

Manifest Management

Method Description Auth
marketplace.list-published List manifests published by this node Local
marketplace.unpublish Remove a published manifest from relays Local

Security Requirements

Container Security Enforcement

Before installing a community app, the node validates:

  1. No latest tag: Image must use a specific version tag
  2. Read-only root: readonly_root must be true (or explicitly overridden by user)
  3. No root: run_as_user must be > 1000
  4. No new privileges: no_new_privileges must be true
  5. Minimal capabilities: Only allowed capabilities are accepted (CHOWN, NET_BIND_SERVICE, etc.)
  6. No host networking: Apps cannot use --network host
  7. Volume restrictions: Apps cannot mount system paths (/, /etc, /var, /usr)

Image Verification

  • Container images are pulled from registries, never transferred between nodes
  • Future: Cosign signature verification for container images (leverages core/security/)
  • Image digest pinning recommended for production apps

UI: Community Marketplace Tab

Route

Extends existing /dashboard/marketplace page.

Layout

Two tabs at the top of Marketplace.vue:

  1. Curated (existing): Built-in apps maintained by Archipelago team
  2. Community (new): Apps discovered from Nostr relays

Community Tab Components

  1. App Grid: Same card layout as curated tab, with trust score badge
  2. Search & Filter: Category filter + text search across community apps
  3. Trust Indicators: Color-coded badges (Verified/Community/Unverified/Untrusted)
  4. App Detail: Shows full manifest, developer DID, relay sources, version history
  5. Install Flow: Trust-level-dependent confirmation (one-click for Verified, warning for Untrusted)

Publishing UI

Accessible from Settings or a "Developer" section:

  1. Select a local app container to publish
  2. Fill in manifest metadata (description, category, icon)
  3. Review security compliance
  4. Sign and publish to relays
  5. View published manifests and their discovery status

Data Storage

/var/lib/archipelago/marketplace/
  ├── cache/
  │   ├── manifests.json      # Cached discovered manifests
  │   └── trust-scores.json   # Cached trust scores
  ├── published/
  │   └── <app-id>.json       # Manifests published by this node
  └── config.json             # Marketplace preferences (auto-refresh interval, etc.)

Implementation Notes

Relay Query Strategy

  1. Query all enabled relays in parallel (from nostr_relays.rs config)
  2. Deduplicate manifests by app_id + version
  3. If same manifest found on multiple relays, boost trust score
  4. Cache results with 15-minute TTL
  5. Background refresh every 30 minutes

Version Comparison

  • Use semantic versioning for all version comparisons
  • When multiple versions exist for the same app_id, show the latest
  • Keep version history available in app detail view
  • Flag apps with versions older than 6 months as potentially unmaintained