- 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>
7.8 KiB
Archipelago App Developer Guide
Build and publish containerized apps for the Archipelago ecosystem.
Overview
Apps run as Podman containers on user nodes. You publish app manifests to Nostr relays, where nodes discover and install them through the community marketplace.
App Manifest
Every app needs a manifest (YAML for local apps, JSON for marketplace publishing).
Template Manifest
# apps/my-app/manifest.yml
app:
id: my-app # Unique, lowercase kebab-case
name: My App
version: 1.0.0 # Semantic versioning
container:
image: docker.io/myorg/my-app:1.0.0 # Never use :latest
ports:
- container: 8080
host: 8180
protocol: tcp
volumes:
- name: data
path: /data
env:
APP_MODE: production
capabilities: [] # Only add if absolutely necessary
readonly_root: true # Required
no_new_privileges: true # Required
run_as_user: 1000 # Must be >= 1000
metadata:
description:
short: "One-line description (max 120 chars)"
long: "Detailed description of what this app does and why."
author:
name: "Your Name"
did: "did:key:z6Mk..." # Your Archipelago node DID
category: money # money | commerce | data | networking | home | community | other
icon_url: "https://example.com/icon.png"
repo_url: "https://github.com/myorg/my-app"
license: MIT
min_archipelago_version: "0.1.0"
dependencies: [] # e.g., ["bitcoin-knots"] if this app needs Bitcoin
Required Fields
| Field | Description |
|---|---|
app.id |
Unique identifier, lowercase, kebab-case only |
app.name |
Human-readable name |
app.version |
Semantic version (major.minor.patch) |
container.image |
Full image reference with pinned version tag |
metadata.description.short |
One-line description, max 120 characters |
metadata.author.did |
Your node's DID (get via node.did RPC) |
Security Requirements
These are enforced by the marketplace and the node. Non-compliant apps are flagged.
Mandatory
- No
:latesttag — Pin a specific version:myapp:1.0.0 - Read-only root filesystem —
readonly_root: true(use volumes for writable data) - Non-root user —
run_as_user: 1000or higher - No privilege escalation —
no_new_privileges: true - Minimal capabilities — Drop all caps, only add required ones
Allowed Capabilities
Only these Linux capabilities may be requested:
| Capability | When Needed |
|---|---|
CHOWN |
App needs to change file ownership |
NET_BIND_SERVICE |
App binds to ports below 1024 |
DAC_OVERRIDE |
App needs to bypass file permissions |
SETUID, SETGID |
App manages user switching (e.g., nginx) |
Forbidden
--network host— Apps cannot share the host network- Mounting system paths:
/,/etc,/var,/usr,/proc,/sys SYS_ADMIN,SYS_PTRACE, or any privileged capability- Hardcoded secrets in environment variables or images
Container Best Practices
Volumes
volumes:
- name: data # App data persists across restarts
path: /data
- name: config # Configuration files
path: /config
Data is stored at /var/lib/archipelago/{app-id}/ on the host.
Health Checks
Define a health check endpoint in your container:
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
Logging
- Log to stdout/stderr (Podman captures container logs)
- Never log secrets, passwords, or keys
- Use structured logging (JSON) for machine parsing
Networking
Apps get their own network namespace. To connect to other Archipelago apps:
# If your app needs to talk to Bitcoin
dependencies:
- bitcoin-knots
container:
env:
BITCOIN_RPC_HOST: bitcoin-knots # Container DNS name on archy-net
BITCOIN_RPC_PORT: "8332"
The archy-net Podman network provides DNS resolution between containers.
Publishing to the Marketplace
1. Build and Push Your Image
podman build -t docker.io/myorg/my-app:1.0.0 .
podman push docker.io/myorg/my-app:1.0.0
2. Get Your Node's DID
curl -b cookies.txt -X POST http://localhost/rpc/v1 \
-d '{"method":"node.did"}'
# Returns: {"result":{"did":"did:key:z6Mk..."}}
3. Publish via RPC
curl -b cookies.txt -X POST http://localhost/rpc/v1 \
-H "Content-Type: application/json" \
-d '{
"method": "marketplace.publish",
"params": {
"app_id": "my-app",
"name": "My App",
"version": "1.0.0",
"description": {"short": "A useful tool", "long": "Detailed description..."},
"author": {"name": "Dev Name", "did": "did:key:z6Mk...", "nostr_pubkey": ""},
"container": {
"image": "docker.io/myorg/my-app:1.0.0",
"ports": [{"container": 8080, "host": 8180, "protocol": "tcp"}],
"volumes": [],
"env": {},
"capabilities": [],
"readonly_root": true,
"no_new_privileges": true,
"run_as_user": 1000
},
"category": "other",
"icon_url": "",
"repo_url": "https://github.com/myorg/my-app",
"license": "MIT",
"min_archipelago_version": "0.1.0",
"dependencies": []
}
}'
The manifest is published to all configured Nostr relays as a NIP-78 event (kind 30078).
4. Verify Discovery
curl -b cookies.txt -X POST http://localhost/rpc/v1 \
-d '{"method":"marketplace.discover"}'
# Your app should appear in the results
Trust Model
Published apps receive trust scores (0-100) based on:
| Factor | Points | How to Maximize |
|---|---|---|
| Valid DID in author | 30 | Always include your node's DID |
| Found on multiple relays | 5-20 | Configure many relays in your node |
| Developer in federation | 20 | Have federated peers who trust you |
| Proper semver version | 10 | Use major.minor.patch format |
| Repository URL present | 5 | Include your repo URL |
| Security compliance | 15 | Meet all security requirements |
Trust Tiers
| Score | Tier | User Experience |
|---|---|---|
| 80-100 | Verified | One-click install |
| 50-79 | Community | Install with confirmation |
| 20-49 | Unverified | Install with warning |
| 0-19 | Untrusted | Requires explicit override |
Testing Your App
Local Testing
# Run your container locally
podman run -d --name my-app \
-p 8180:8080 \
--read-only \
--security-opt no-new-privileges \
--user 1000:1000 \
docker.io/myorg/my-app:1.0.0
# Verify it works
curl http://localhost:8180/health
# Check logs
podman logs my-app
On an Archipelago Node
- Install via the marketplace UI or RPC:
curl -b cookies.txt -X POST http://192.168.1.228/rpc/v1 \ -d '{"method":"package.install","params":{"id":"my-app","dockerImage":"docker.io/myorg/my-app:1.0.0"}}' - Verify the container is running:
curl -b cookies.txt -X POST http://192.168.1.228/rpc/v1 \ -d '{"method":"container-list"}' - Check the UI at
http://192.168.1.228/app/my-app/
Validate Manifest
curl -b cookies.txt -X POST http://localhost/rpc/v1 \
-H "Content-Type: application/json" \
-d '{"method":"marketplace.verify","params":{...your manifest...}}'
# Returns: {"result":{"valid":true,"issues":[],"trust_score":65,"trust_tier":"community"}}
Updating Your App
- Build and push the new version:
docker.io/myorg/my-app:1.1.0 - Publish an updated manifest with the new version
- NIP-33 replaceable events: the latest publish overwrites the previous one on relays
- Nodes running your app can see the update in their marketplace
App Icon
- Provide a URL to your app icon (PNG, WebP, or SVG)
- Recommended size: 256x256 pixels
- Square aspect ratio
- If no icon URL, a generic placeholder is shown in the marketplace