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>
This commit is contained in:
331
docs/marketplace-protocol.md
Normal file
331
docs/marketplace-protocol.md
Normal file
@@ -0,0 +1,331 @@
|
||||
# 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
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
```json
|
||||
{
|
||||
"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
|
||||
Reference in New Issue
Block a user