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:
277
docs/app-developer-guide.md
Normal file
277
docs/app-developer-guide.md
Normal file
@@ -0,0 +1,277 @@
|
||||
# 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
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
1. **No `:latest` tag** — Pin a specific version: `myapp:1.0.0`
|
||||
2. **Read-only root filesystem** — `readonly_root: true` (use volumes for writable data)
|
||||
3. **Non-root user** — `run_as_user: 1000` or higher
|
||||
4. **No privilege escalation** — `no_new_privileges: true`
|
||||
5. **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
|
||||
|
||||
```yaml
|
||||
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:
|
||||
|
||||
```dockerfile
|
||||
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:
|
||||
|
||||
```yaml
|
||||
# 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
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
curl -b cookies.txt -X POST http://localhost/rpc/v1 \
|
||||
-d '{"method":"node.did"}'
|
||||
# Returns: {"result":{"did":"did:key:z6Mk..."}}
|
||||
```
|
||||
|
||||
### 3. Publish via RPC
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
1. Install via the marketplace UI or RPC:
|
||||
```bash
|
||||
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"}}'
|
||||
```
|
||||
2. Verify the container is running:
|
||||
```bash
|
||||
curl -b cookies.txt -X POST http://192.168.1.228/rpc/v1 \
|
||||
-d '{"method":"container-list"}'
|
||||
```
|
||||
3. Check the UI at `http://192.168.1.228/app/my-app/`
|
||||
|
||||
### Validate Manifest
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
1. Build and push the new version: `docker.io/myorg/my-app:1.1.0`
|
||||
2. Publish an updated manifest with the new version
|
||||
3. NIP-33 replaceable events: the latest publish overwrites the previous one on relays
|
||||
4. 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
|
||||
Reference in New Issue
Block a user