app-platform: generate catalog from app manifests

This commit is contained in:
archipelago 2026-06-11 00:24:20 -04:00
parent 9079d404d6
commit 09ec64932f
30 changed files with 1533 additions and 235 deletions

View File

@ -14,7 +14,7 @@
"id": "bitcoin-knots", "id": "bitcoin-knots",
"title": "Bitcoin Knots", "title": "Bitcoin Knots",
"version": "28.1.0", "version": "28.1.0",
"description": "Run a full Bitcoin node. Validate and relay blocks and transactions.", "description": "Full Bitcoin Knots node with dynamic prune/full-mode startup based on host disk.",
"icon": "/assets/img/app-icons/bitcoin-knots.webp", "icon": "/assets/img/app-icons/bitcoin-knots.webp",
"author": "Bitcoin Knots", "author": "Bitcoin Knots",
"category": "money", "category": "money",
@ -25,8 +25,8 @@
{ {
"id": "bitcoin-core", "id": "bitcoin-core",
"title": "Bitcoin Core", "title": "Bitcoin Core",
"version": "28.4", "version": "28.4.0",
"description": "Reference Bitcoin node implementation. Alternative to Bitcoin Knots; uninstall Knots before switching.", "description": "Reference Bitcoin Core node with dynamic prune/full-mode startup based on host disk.",
"icon": "/assets/img/app-icons/bitcoin-core.svg", "icon": "/assets/img/app-icons/bitcoin-core.svg",
"author": "Bitcoin Core contributors", "author": "Bitcoin Core contributors",
"category": "money", "category": "money",
@ -38,7 +38,7 @@
"id": "lnd", "id": "lnd",
"title": "LND", "title": "LND",
"version": "0.18.4", "version": "0.18.4",
"description": "Lightning Network Daemon. Fast Bitcoin payments through Lightning.", "description": "Lightning Network implementation by Lightning Labs. Enables instant, low-cost Bitcoin payments.",
"icon": "/assets/img/app-icons/lnd.svg", "icon": "/assets/img/app-icons/lnd.svg",
"author": "Lightning Labs", "author": "Lightning Labs",
"category": "money", "category": "money",
@ -53,7 +53,7 @@
"id": "btcpay-server", "id": "btcpay-server",
"title": "BTCPay Server", "title": "BTCPay Server",
"version": "2.3.9", "version": "2.3.9",
"description": "Self-hosted Bitcoin payment processor.", "description": "Self-hosted Bitcoin payment processor. Accept Bitcoin payments without intermediaries.",
"icon": "/assets/img/app-icons/btcpay-server.png", "icon": "/assets/img/app-icons/btcpay-server.png",
"author": "BTCPay Server Foundation", "author": "BTCPay Server Foundation",
"category": "commerce", "category": "commerce",
@ -76,8 +76,17 @@
"dockerImage": "ghcr.io/saleor/saleor:3.23", "dockerImage": "ghcr.io/saleor/saleor:3.23",
"repoUrl": "https://github.com/saleor/saleor", "repoUrl": "https://github.com/saleor/saleor",
"containerConfig": { "containerConfig": {
"ports": ["9011:80", "9010:80", "8000:8000", "8025:8025", "16686:16686"], "ports": [
"volumes": ["/var/lib/archipelago/saleor:/app/media", "/var/lib/archipelago/saleor-db:/var/lib/postgresql/data"], "9011:80",
"9010:80",
"8000:8000",
"8025:8025",
"16686:16686"
],
"volumes": [
"/var/lib/archipelago/saleor:/app/media",
"/var/lib/archipelago/saleor-db:/var/lib/postgresql/data"
],
"notes": "Installed as a Saleor stack: customer storefront on 9011, admin dashboard on 9010, API on 8000, Mailpit on 8025, and Jaeger on 16686. Supporting containers include PostgreSQL, Valkey, Celery worker, and services required by Saleor." "notes": "Installed as a Saleor stack: customer storefront on 9011, admin dashboard on 9010, API on 8000, Mailpit on 8025, and Jaeger on 16686. Supporting containers include PostgreSQL, Valkey, Celery worker, and services required by Saleor."
} }
}, },
@ -85,7 +94,7 @@
"id": "mempool", "id": "mempool",
"title": "Mempool Explorer", "title": "Mempool Explorer",
"version": "3.0.0", "version": "3.0.0",
"description": "Self-hosted Bitcoin blockchain and mempool visualizer.", "description": "Bitcoin mempool and blockchain explorer. Real-time transaction and block visualization.",
"icon": "/assets/img/app-icons/mempool.webp", "icon": "/assets/img/app-icons/mempool.webp",
"author": "Mempool", "author": "Mempool",
"category": "money", "category": "money",
@ -101,7 +110,7 @@
"id": "electrumx", "id": "electrumx",
"title": "ElectrumX", "title": "ElectrumX",
"version": "1.18.0", "version": "1.18.0",
"description": "Electrum protocol server. Index the blockchain for fast wallet lookups.", "description": "Electrum server indexing Bitcoin chain data for lightweight wallet queries.",
"icon": "/assets/img/app-icons/electrumx.png", "icon": "/assets/img/app-icons/electrumx.png",
"author": "Luke Childs", "author": "Luke Childs",
"category": "money", "category": "money",
@ -116,7 +125,7 @@
"id": "indeedhub", "id": "indeedhub",
"title": "IndeeHub", "title": "IndeeHub",
"version": "1.0.0", "version": "1.0.0",
"description": "Bitcoin documentary streaming with Nostr identity.", "description": "Bitcoin documentary streaming platform featuring God Bless Bitcoin and other educational content about Bitcoin, sovereignty, and decentralized technology. Sign in with your Nostr identity.",
"icon": "/assets/img/app-icons/indeedhub.png", "icon": "/assets/img/app-icons/indeedhub.png",
"author": "IndeeHub", "author": "IndeeHub",
"category": "community", "category": "community",
@ -127,49 +136,133 @@
"id": "botfights", "id": "botfights",
"title": "BotFights", "title": "BotFights",
"version": "1.1.0", "version": "1.1.0",
"description": "Bot arena + 2-player arcade fighter with controller support and Adventure Mode.", "description": "Bot competition arena with 2-player arcade fighting mode. AI bots battle in trivia challenges while humans duke it out with controllers. Built for Bitcoiners.",
"icon": "/assets/img/app-icons/botfights.svg", "icon": "/assets/img/app-icons/botfights.svg",
"author": "BotFights", "author": "BotFights",
"category": "community", "category": "community",
"dockerImage": "146.59.87.168:3000/lfg2025/botfights:1.1.0", "dockerImage": "146.59.87.168:3000/lfg2025/botfights:1.1.0",
"repoUrl": "https://botfights.net", "repoUrl": "https://botfights.net",
"containerConfig": { "containerConfig": {
"ports": ["9100:9100"], "ports": [
"volumes": ["/var/lib/archipelago/botfights:/app/server/data"], "9100:9100"
"env": ["NODE_ENV=production", "PORT=9100", "FIGHT_LOOP_ENABLED=true", "ARCHY_EMBEDDED=1"] ],
"volumes": [
"/var/lib/archipelago/botfights:/app/server/data"
],
"env": [
"NODE_ENV=production",
"PORT=9100",
"FIGHT_LOOP_ENABLED=true",
"ARCHY_EMBEDDED=1"
]
} }
}, },
{ {
"id": "gitea", "id": "gitea",
"title": "Gitea", "title": "Gitea",
"version": "1.23", "version": "1.23",
"description": "Self-hosted Git service with container registry, CI/CD, issue tracking.", "description": "Self-hosted Git service with built-in container registry, CI/CD, and package hosting.",
"icon": "/assets/img/app-icons/gitea.svg", "icon": "/assets/img/app-icons/gitea.svg",
"author": "Gitea", "author": "Gitea",
"category": "development", "category": "development",
"dockerImage": "146.59.87.168:3000/lfg2025/gitea:1.23", "dockerImage": "docker.io/gitea/gitea:1.23",
"repoUrl": "https://gitea.com", "repoUrl": "https://gitea.com",
"containerConfig": { "containerConfig": {
"ports": ["3001:3000", "2222:22"], "ports": [
"volumes": ["/var/lib/archipelago/gitea/data:/data", "/var/lib/archipelago/gitea/config:/etc/gitea"], "3001:3000",
"env": ["GITEA__database__DB_TYPE=sqlite3", "GITEA__server__SSH_PORT=2222", "GITEA__server__SSH_LISTEN_PORT=22", "GITEA__server__LFS_START_SERVER=true", "GITEA__packages__ENABLED=true", "GITEA__repository__ENABLE_PUSH_CREATE_USER=true", "GITEA__repository__ENABLE_PUSH_CREATE_ORG=true", "GITEA__security__X_FRAME_OPTIONS="] "2222:22"
} ],
"volumes": [
"/var/lib/archipelago/gitea/data:/data",
"/var/lib/archipelago/gitea/config:/etc/gitea"
],
"env": [
"GITEA__database__DB_TYPE=sqlite3",
"GITEA__server__SSH_PORT=2222",
"GITEA__server__SSH_LISTEN_PORT=22",
"GITEA__server__LFS_START_SERVER=true",
"GITEA__packages__ENABLED=true",
"GITEA__repository__ENABLE_PUSH_CREATE_USER=true",
"GITEA__repository__ENABLE_PUSH_CREATE_ORG=true",
"GITEA__security__X_FRAME_OPTIONS="
]
},
"tier": "optional"
}, },
{ {
"id": "filebrowser", "id": "filebrowser",
"title": "File Browser", "title": "File Browser",
"version": "2.27.0", "version": "2.27.0",
"description": "Web-based file manager.", "description": "Baseline Archipelago file manager service.",
"icon": "/assets/img/app-icons/file-browser.webp", "icon": "/assets/img/app-icons/file-browser.webp",
"author": "File Browser", "author": "File Browser",
"category": "data", "category": "data",
"tier": "core", "tier": "core",
"dockerImage": "146.59.87.168:3000/lfg2025/filebrowser:v2.27.0", "dockerImage": "git.tx1138.com/lfg2025/filebrowser:v2.27.0",
"repoUrl": "https://github.com/filebrowser/filebrowser", "repoUrl": "https://github.com/filebrowser/filebrowser",
"containerConfig": { "containerConfig": {
"ports": ["8083:80"], "ports": [
"volumes": ["/var/lib/archipelago/filebrowser:/srv", "/var/lib/archipelago/filebrowser-data:/data"], "8083:80"
"args": ["--database=/data/database.db", "--root=/srv", "--address=0.0.0.0", "--port=80"] ],
"volumes": [
"/var/lib/archipelago/filebrowser:/srv",
"/var/lib/archipelago/filebrowser-data:/data"
],
"args": [
"--database=/data/database.db",
"--root=/srv",
"--address=0.0.0.0",
"--port=80"
]
}
},
{
"id": "nostr-rs-relay",
"title": "Nostr Relay (Rust)",
"version": "0.8.0",
"description": "High-performance Nostr relay written in Rust. Host your own decentralized social media relay and earn networking profits.",
"icon": "/assets/img/app-icons/nostr.svg",
"author": "Nostr RS Relay",
"category": "community",
"tier": "recommended",
"dockerImage": "scsibug/nostr-rs-relay:0.8.9",
"repoUrl": "https://github.com/scsibug/nostr-rs-relay",
"containerConfig": {
"ports": [
"8081:8080"
],
"volumes": [
"/var/lib/archipelago/nostr-relay:/usr/src/app/db"
],
"env": [
"RELAY_NAME=Archipelago Nostr Relay",
"RELAY_DESCRIPTION=Self-hosted Nostr relay on Archipelago"
]
}
},
{
"id": "meshtastic",
"title": "Meshtastic",
"version": "2-daily-alpine",
"description": "Open-source mesh networking for LoRa radios. Create decentralized communication networks.",
"icon": "/assets/img/app-icons/meshcore.svg",
"author": "Meshtastic",
"category": "networking",
"tier": "recommended",
"dockerImage": "docker.io/meshtastic/meshtasticd:daily-alpine",
"repoUrl": "https://github.com/meshtastic/firmware",
"containerConfig": {
"ports": [
"4403:4403"
],
"volumes": [
"/var/lib/archipelago/meshtastic:/var/lib/meshtasticd"
],
"env": [
"MESHTASTIC_PORT=/dev/ttyUSB0",
"MESHTASTIC_SERIAL=true"
],
"notes": "Requires a LoRa radio device at /dev/ttyUSB0. The config file is rendered from the app manifest before container start."
} }
}, },
{ {
@ -184,15 +277,19 @@
"dockerImage": "146.59.87.168:3000/lfg2025/vaultwarden:1.30.0-alpine", "dockerImage": "146.59.87.168:3000/lfg2025/vaultwarden:1.30.0-alpine",
"repoUrl": "https://github.com/dani-garcia/vaultwarden", "repoUrl": "https://github.com/dani-garcia/vaultwarden",
"containerConfig": { "containerConfig": {
"ports": ["8082:80"], "ports": [
"volumes": ["/var/lib/archipelago/vaultwarden:/data"] "8082:80"
],
"volumes": [
"/var/lib/archipelago/vaultwarden:/data"
]
} }
}, },
{ {
"id": "searxng", "id": "searxng",
"title": "SearXNG", "title": "SearXNG",
"version": "2024.1.0", "version": "1.0.0",
"description": "Privacy-respecting metasearch engine.", "description": "Privacy-respecting metasearch engine. Search the web without tracking.",
"icon": "/assets/img/app-icons/searxng.png", "icon": "/assets/img/app-icons/searxng.png",
"author": "SearXNG", "author": "SearXNG",
"category": "data", "category": "data",
@ -200,21 +297,46 @@
"dockerImage": "146.59.87.168:3000/lfg2025/searxng:latest", "dockerImage": "146.59.87.168:3000/lfg2025/searxng:latest",
"repoUrl": "https://github.com/searxng/searxng", "repoUrl": "https://github.com/searxng/searxng",
"containerConfig": { "containerConfig": {
"ports": ["8888:8080"], "ports": [
"volumes": ["/var/lib/archipelago/searxng:/etc/searxng"] "8888:8080"
],
"volumes": [
"/var/lib/archipelago/searxng:/etc/searxng"
]
} }
}, },
{ {
"id": "fedimint", "id": "fedimint",
"title": "Fedimint", "title": "Fedimint",
"version": "0.10.0", "version": "0.10.0",
"description": "Federated Bitcoin mint with privacy through federated guardians.", "description": "Federated Bitcoin minting service with built-in Guardian UI. Privacy-preserving Bitcoin custody.",
"icon": "/assets/img/app-icons/fedimint.png", "icon": "/assets/img/app-icons/fedimint.png",
"author": "Fedimint", "author": "Fedimint",
"category": "money", "category": "money",
"dockerImage": "146.59.87.168:3000/lfg2025/fedimintd:v0.10.0", "dockerImage": "146.59.87.168:3000/lfg2025/fedimintd:v0.10.0",
"repoUrl": "https://github.com/fedimint/fedimint" "repoUrl": "https://github.com/fedimint/fedimint"
}, },
{
"id": "fedimint-gateway",
"title": "Fedimint Gateway",
"version": "0.10.0",
"description": "Fedimint gateway service with automatic LND-or-LDK backend selection.",
"icon": "/assets/img/app-icons/fedimint.png",
"author": "Fedimint",
"category": "money",
"dockerImage": "git.tx1138.com/lfg2025/gatewayd:v0.10.0",
"repoUrl": "https://github.com/fedimint/fedimint",
"containerConfig": {
"ports": [
"8176:8176",
"9737:9737"
],
"volumes": [
"/var/lib/archipelago/fedimint-gateway:/data",
"/var/lib/archipelago/lnd:/lnd:ro"
]
}
},
{ {
"id": "jellyfin", "id": "jellyfin",
"title": "Jellyfin", "title": "Jellyfin",
@ -226,8 +348,13 @@
"dockerImage": "146.59.87.168:3000/lfg2025/jellyfin:10.8.13", "dockerImage": "146.59.87.168:3000/lfg2025/jellyfin:10.8.13",
"repoUrl": "https://github.com/jellyfin/jellyfin", "repoUrl": "https://github.com/jellyfin/jellyfin",
"containerConfig": { "containerConfig": {
"ports": ["8096:8096"], "ports": [
"volumes": ["/var/lib/archipelago/jellyfin/config:/config", "/var/lib/archipelago/jellyfin/cache:/cache"] "8096:8096"
],
"volumes": [
"/var/lib/archipelago/jellyfin/config:/config",
"/var/lib/archipelago/jellyfin/cache:/cache"
]
} }
}, },
{ {
@ -244,34 +371,47 @@
{ {
"id": "homeassistant", "id": "homeassistant",
"title": "Home Assistant", "title": "Home Assistant",
"version": "2024.1", "version": "2024.1.0",
"description": "Open-source home automation.", "description": "Open source home automation platform. Control and monitor your smart home devices.",
"icon": "/assets/img/app-icons/homeassistant.png", "icon": "/assets/img/app-icons/homeassistant.png",
"author": "Home Assistant", "author": "Home Assistant",
"category": "home", "category": "home",
"dockerImage": "146.59.87.168:3000/lfg2025/home-assistant:2024.1", "dockerImage": "146.59.87.168:3000/lfg2025/home-assistant:2024.1",
"repoUrl": "https://github.com/home-assistant/core", "repoUrl": "https://github.com/home-assistant/core",
"containerConfig": { "containerConfig": {
"ports": ["8123:8123"], "ports": [
"volumes": ["/var/lib/archipelago/home-assistant:/config"], "8123:8123"
"env": ["TZ=UTC"] ],
"volumes": [
"/var/lib/archipelago/home-assistant:/config"
],
"env": [
"TZ=UTC"
]
} }
}, },
{ {
"id": "grafana", "id": "grafana",
"title": "Grafana", "title": "Grafana",
"version": "10.2.0", "version": "10.2.0",
"description": "Analytics and monitoring dashboards.", "description": "Analytics and monitoring platform. Visualize metrics and create dashboards.",
"icon": "/assets/img/app-icons/grafana.png", "icon": "/assets/img/app-icons/grafana.png",
"author": "Grafana Labs", "author": "Grafana Labs",
"category": "data", "category": "data",
"tier": "recommended", "tier": "recommended",
"dockerImage": "146.59.87.168:3000/lfg2025/grafana:10.2.0", "dockerImage": "grafana/grafana:10.2.0",
"repoUrl": "https://github.com/grafana/grafana", "repoUrl": "https://github.com/grafana/grafana",
"containerConfig": { "containerConfig": {
"ports": ["3000:3000"], "ports": [
"volumes": ["/var/lib/archipelago/grafana:/var/lib/grafana"], "3000:3000"
"env": ["GF_PATHS_DATA=/var/lib/grafana", "GF_USERS_ALLOW_SIGN_UP=false"] ],
"volumes": [
"/var/lib/archipelago/grafana:/var/lib/grafana"
],
"env": [
"GF_PATHS_DATA=/var/lib/grafana",
"GF_USERS_ALLOW_SIGN_UP=false"
]
} }
}, },
{ {
@ -286,10 +426,42 @@
"dockerImage": "146.59.87.168:3000/lfg2025/tailscale:stable", "dockerImage": "146.59.87.168:3000/lfg2025/tailscale:stable",
"repoUrl": "https://github.com/tailscale/tailscale", "repoUrl": "https://github.com/tailscale/tailscale",
"containerConfig": { "containerConfig": {
"ports": ["8240:8240"], "ports": [
"volumes": ["/var/lib/archipelago/tailscale:/var/lib/tailscale"], "8240:8240"
"env": ["TS_STATE_DIR=/var/lib/tailscale"], ],
"args": ["sh", "-c", "tailscaled --tun=userspace-networking & sleep 2; tailscale web --listen 0.0.0.0:8240 & wait"] "volumes": [
"/var/lib/archipelago/tailscale:/var/lib/tailscale"
],
"env": [
"TS_STATE_DIR=/var/lib/tailscale"
],
"args": [
"sh",
"-c",
"tailscaled --tun=userspace-networking & for i in $(seq 1 30); do [ -S /var/run/tailscale/tailscaled.sock ] && break; sleep 1; done; tailscale web --listen 0.0.0.0:8240 & wait"
]
}
},
{
"id": "portainer",
"title": "Portainer",
"version": "2.19.4",
"description": "Container management web UI for the local Podman socket.",
"icon": "/assets/img/app-icons/portainer.webp",
"author": "Portainer",
"category": "development",
"tier": "optional",
"dockerImage": "146.59.87.168:3000/lfg2025/portainer:latest",
"repoUrl": "https://github.com/portainer/portainer",
"containerConfig": {
"ports": [
"9000:9000"
],
"volumes": [
"/var/lib/archipelago/portainer:/data",
"/run/user/1000/podman/podman.sock:/var/run/docker.sock"
],
"notes": "Uses the manifest-owned Podman socket bind mount preparation path."
} }
}, },
{ {
@ -304,8 +476,14 @@
"dockerImage": "docker.io/netbirdio/dashboard:v2.38.0", "dockerImage": "docker.io/netbirdio/dashboard:v2.38.0",
"repoUrl": "https://github.com/netbirdio/netbird", "repoUrl": "https://github.com/netbirdio/netbird",
"containerConfig": { "containerConfig": {
"ports": ["8087:80", "8086:80", "3478:3478/udp"], "ports": [
"volumes": ["/var/lib/archipelago/netbird:/var/lib/netbird"], "8087:80",
"8086:80",
"3478:3478/udp"
],
"volumes": [
"/var/lib/archipelago/netbird:/var/lib/netbird"
],
"notes": "Installed as a two-container stack: netbird dashboard on 8087 and netbird-server control plane on 8086 plus UDP 3478. For production clients, publish a DNS name over HTTPS with gRPC/WebSocket routing." "notes": "Installed as a two-container stack: netbird dashboard on 8087 and netbird-server control plane on 8086 plus UDP 3478. For production clients, publish a DNS name over HTTPS with gRPC/WebSocket routing."
} }
}, },
@ -321,10 +499,20 @@
"dockerImage": "146.59.87.168:3000/lfg2025/uptime-kuma:1", "dockerImage": "146.59.87.168:3000/lfg2025/uptime-kuma:1",
"repoUrl": "https://github.com/louislam/uptime-kuma", "repoUrl": "https://github.com/louislam/uptime-kuma",
"containerConfig": { "containerConfig": {
"ports": ["3002:3001"], "ports": [
"volumes": ["/var/lib/archipelago/uptime-kuma:/app/data"], "3002:3001"
"env": ["TZ=UTC"], ],
"args": ["--", "node", "server/server.js"] "volumes": [
"/var/lib/archipelago/uptime-kuma:/app/data"
],
"env": [
"TZ=UTC"
],
"args": [
"--",
"node",
"server/server.js"
]
} }
}, },
{ {
@ -338,24 +526,35 @@
"dockerImage": "146.59.87.168:3000/lfg2025/photoprism:240915", "dockerImage": "146.59.87.168:3000/lfg2025/photoprism:240915",
"repoUrl": "https://github.com/photoprism/photoprism", "repoUrl": "https://github.com/photoprism/photoprism",
"containerConfig": { "containerConfig": {
"ports": ["2342:2342"], "ports": [
"volumes": ["/var/lib/archipelago/photoprism:/photoprism/storage"], "2342:2342"
"env": ["PHOTOPRISM_ADMIN_PASSWORD=archipelago", "PHOTOPRISM_DEFAULT_LOCALE=en"] ],
"volumes": [
"/var/lib/archipelago/photoprism:/photoprism/storage"
],
"env": [
"PHOTOPRISM_ADMIN_PASSWORD=archipelago",
"PHOTOPRISM_DEFAULT_LOCALE=en"
]
} }
}, },
{ {
"id": "nextcloud", "id": "nextcloud",
"title": "Nextcloud", "title": "Nextcloud",
"version": "28", "version": "29",
"description": "Your own private cloud. File sync, calendars, contacts.", "description": "Your own private cloud. File sync, calendars, contacts.",
"icon": "/assets/img/app-icons/nextcloud.webp", "icon": "/assets/img/app-icons/nextcloud.webp",
"author": "Nextcloud", "author": "Nextcloud",
"category": "data", "category": "data",
"dockerImage": "146.59.87.168:3000/lfg2025/nextcloud:28", "dockerImage": "146.59.87.168:3000/lfg2025/nextcloud:29",
"repoUrl": "https://github.com/nextcloud/server", "repoUrl": "https://github.com/nextcloud/server",
"containerConfig": { "containerConfig": {
"ports": ["8085:80"], "ports": [
"volumes": ["/var/lib/archipelago/nextcloud:/var/www/html"] "8085:80"
],
"volumes": [
"/var/lib/archipelago/nextcloud:/var/www/html"
]
} }
} }
] ]

View File

@ -56,8 +56,8 @@ app:
endpoint: http://localhost:32838 endpoint: http://localhost:32838
path: / path: /
interval: 30s interval: 30s
timeout: 5s timeout: 30s
retries: 3 retries: 5
bitcoin_integration: bitcoin_integration:
rpc_access: read-only rpc_access: read-only

View File

@ -28,11 +28,17 @@ app:
fi; fi;
RPC_USER="$(printenv BITCOIN_RPC_USER)"; RPC_USER="$(printenv BITCOIN_RPC_USER)";
RPC_PASS="$(printenv BITCOIN_RPC_PASS)"; RPC_PASS="$(printenv BITCOIN_RPC_PASS)";
RPC_TXRELAY_AUTH="$(printenv BITCOIN_RPC_TXRELAY_RPCAUTH || true)";
DISK_GB_VALUE="$(printenv DISK_GB || true)"; DISK_GB_VALUE="$(printenv DISK_GB || true)";
RPC_HEADROOM="-rpcthreads=16 -rpcworkqueue=256";
RPC_TXRELAY_FLAGS="-rpcwhitelistdefault=0";
if [ -n "$RPC_TXRELAY_AUTH" ]; then
RPC_TXRELAY_FLAGS="$RPC_TXRELAY_FLAGS -rpcauth=$RPC_TXRELAY_AUTH -rpcwhitelist=txrelay:sendrawtransaction,testmempoolaccept,getmempoolinfo,getrawmempool,getmempoolentry,getnetworkinfo,getblockchaininfo,getblockcount,getblockhash,getblockheader,getrawtransaction,decoderawtransaction,decodescript,estimatesmartfee";
fi;
if [ "${DISK_GB_VALUE:-0}" -lt 1000 ]; then if [ "${DISK_GB_VALUE:-0}" -lt 1000 ]; then
exec "$BITCOIND" -datadir=/home/bitcoin/.bitcoin -noconf -server=1 -prune=550 -rpcallowip=0.0.0.0/0 -rpcbind=0.0.0.0:8332 -listen=1 -bind=0.0.0.0:8333 -dbcache=2048 -par=0 -maxconnections=125 -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS"; exec "$BITCOIND" -datadir=/home/bitcoin/.bitcoin -noconf -server=1 -prune=550 -rpcallowip=0.0.0.0/0 -rpcbind=0.0.0.0:8332 -listen=1 -bind=0.0.0.0:8333 -dbcache=2048 -par=0 -maxconnections=125 $RPC_HEADROOM $RPC_TXRELAY_FLAGS -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS";
else else
exec "$BITCOIND" -datadir=/home/bitcoin/.bitcoin -noconf -server=1 -txindex=1 -rpcallowip=0.0.0.0/0 -rpcbind=0.0.0.0:8332 -listen=1 -bind=0.0.0.0:8333 -dbcache=4096 -par=0 -maxconnections=125 -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS"; exec "$BITCOIND" -datadir=/home/bitcoin/.bitcoin -noconf -server=1 -txindex=1 -rpcallowip=0.0.0.0/0 -rpcbind=0.0.0.0:8332 -listen=1 -bind=0.0.0.0:8333 -dbcache=4096 -par=0 -maxconnections=125 $RPC_HEADROOM $RPC_TXRELAY_FLAGS -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS";
fi fi
derived_env: derived_env:
- key: DISK_GB - key: DISK_GB
@ -40,6 +46,8 @@ app:
secret_env: secret_env:
- key: BITCOIN_RPC_PASS - key: BITCOIN_RPC_PASS
secret_file: bitcoin-rpc-password secret_file: bitcoin-rpc-password
- key: BITCOIN_RPC_TXRELAY_RPCAUTH
secret_file: bitcoin-rpc-txrelay-rpcauth
data_uid: "100101:100101" data_uid: "100101:100101"
dependencies: dependencies:

View File

@ -1,12 +1,12 @@
app: app:
id: botfights id: botfights
name: BotFights name: BotFights
version: 1.0.0 version: 1.1.0
description: Bot competition arena with 2-player arcade fighting mode. AI bots battle in trivia challenges while humans duke it out with controllers. Built for Bitcoiners. description: Bot competition arena with 2-player arcade fighting mode. AI bots battle in trivia challenges while humans duke it out with controllers. Built for Bitcoiners.
category: community category: community
container: container:
image: git.tx1138.com/lfg2025/botfights:1.1.0 image: 146.59.87.168:3000/lfg2025/botfights:1.1.0
pull_policy: always pull_policy: always
dependencies: dependencies:
@ -62,6 +62,8 @@ app:
metadata: metadata:
author: Dorian author: Dorian
repo: https://botfights.net
icon: /assets/img/app-icons/botfights.svg
license: MIT license: MIT
tags: tags:
- bitcoin - bitcoin

View File

@ -60,8 +60,8 @@ app:
endpoint: http://localhost:49392 endpoint: http://localhost:49392
path: / path: /
interval: 30s interval: 30s
timeout: 5s timeout: 30s
retries: 3 retries: 5
bitcoin_integration: bitcoin_integration:
rpc_access: read-only rpc_access: read-only
@ -79,3 +79,7 @@ app:
port: 23000 port: 23000
protocol: http protocol: http
path: / path: /
metadata:
launch:
open_in_new_tab: true

View File

@ -5,7 +5,7 @@ app:
description: Electrum server indexing Bitcoin chain data for lightweight wallet queries. description: Electrum server indexing Bitcoin chain data for lightweight wallet queries.
container: container:
image: git.tx1138.com/lfg2025/electrumx:v1.18.0 image: 146.59.87.168:3000/lfg2025/electrumx:v1.18.0
pull_policy: if-not-present pull_policy: if-not-present
network: archy-net network: archy-net
data_uid: "1000:1000" data_uid: "1000:1000"

View File

@ -5,9 +5,17 @@ app:
description: Federated Bitcoin minting service with built-in Guardian UI. Privacy-preserving Bitcoin custody. description: Federated Bitcoin minting service with built-in Guardian UI. Privacy-preserving Bitcoin custody.
container: container:
image: git.tx1138.com/lfg2025/fedimintd:v0.10.0 image: 146.59.87.168:3000/lfg2025/fedimintd:v0.10.0
pull_policy: if-not-present pull_policy: if-not-present
network: archy-net network: archy-net
entrypoint: ["sh", "-lc"]
custom_args:
- |-
until state="$(curl -sS --connect-timeout 5 -m 45 -u "$FM_BITCOIND_USERNAME:$FM_BITCOIND_PASSWORD" -H "Content-Type: application/json" --data-binary '{"jsonrpc":"1.0","id":"fedimint-wait","method":"getblockchaininfo","params":[]}' "$FM_BITCOIND_URL/")" && echo "$state" | grep -q '"initialblockdownload":false'; do
echo "Waiting for Bitcoin RPC sync at $FM_BITCOIND_URL...";
sleep 30;
done;
exec fedimintd
derived_env: derived_env:
- key: FM_P2P_URL - key: FM_P2P_URL
template: fedimint://{{HOST_MDNS}}:8173 template: fedimint://{{HOST_MDNS}}:8173
@ -40,7 +48,9 @@ app:
- host: 8174 - host: 8174
container: 8174 container: 8174
protocol: tcp protocol: tcp
- host: 8175 # Public launch port 8175 is owned by archy-fedimint-ui, which serves a
# wait page while Bitcoin syncs and proxies here after fedimintd starts.
- host: 8177
container: 8175 container: 8175
protocol: tcp protocol: tcp
@ -52,7 +62,7 @@ app:
environment: environment:
- FM_DATA_DIR=/data - FM_DATA_DIR=/data
- FM_BITCOIND_URL=http://host.archipelago:8332 - FM_BITCOIND_URL=http://bitcoin-knots:8332
- FM_BITCOIND_USERNAME=archipelago - FM_BITCOIND_USERNAME=archipelago
- FM_BITCOIN_NETWORK=bitcoin - FM_BITCOIN_NETWORK=bitcoin
- FM_BIND_P2P=0.0.0.0:8173 - FM_BIND_P2P=0.0.0.0:8173
@ -67,6 +77,15 @@ app:
timeout: 5s timeout: 5s
retries: 3 retries: 3
interfaces:
main:
name: Guardian UI
description: Fedimint Guardian wait/proxy UI
type: ui
port: 8175
protocol: http
path: /
bitcoin_integration: bitcoin_integration:
rpc_access: admin rpc_access: admin
sync_required: true sync_required: true

View File

@ -1,52 +1,87 @@
id: gitea app:
name: Gitea id: gitea
version: "1.23" name: Gitea
description: Self-hosted Git service with built-in container registry, CI/CD, and package hosting. version: "1.23"
category: development description: Self-hosted Git service with built-in container registry, CI/CD, and package hosting.
icon: git-branch category: development
port: 3000
internal_port: 3001
ssh_port: 2222
image: docker.io/gitea/gitea:1.23
tier: optional
requires: container:
memory_mb: 256 image: docker.io/gitea/gitea:1.23
disk_mb: 500 pull_policy: if-not-present
volumes: dependencies:
- host: /var/lib/archipelago/gitea/data - storage: 500Mi
container: /data
- host: /var/lib/archipelago/gitea/config
container: /etc/gitea
environment: resources:
GITEA__database__DB_TYPE: sqlite3 memory_limit: 256Mi
GITEA__server__SSH_PORT: "2222" disk_limit: 500Mi
GITEA__server__SSH_LISTEN_PORT: "22"
GITEA__server__LFS_START_SERVER: "true"
GITEA__packages__ENABLED: "true"
GITEA__repository__ENABLE_PUSH_CREATE_USER: "true"
GITEA__repository__ENABLE_PUSH_CREATE_ORG: "true"
# Gitea hardcodes X-Frame-Options: SAMEORIGIN, so Archipelago opens it in a security:
# new tab on host port 3001 instead of embedding it in an iframe. capabilities: [CHOWN, FOWNER, SETUID, SETGID, DAC_OVERRIDE, NET_BIND_SERVICE]
nginx_proxy: readonly_root: false
listen: 3000 no_new_privileges: false
proxy_pass: "http://127.0.0.1:3001" network_policy: bridge
extra_headers:
- "proxy_hide_header X-Frame-Options"
- "proxy_hide_header Content-Security-Policy"
health_check: ports:
endpoint: / - host: 3001
interval: 120 container: 3000
timeout: 5 protocol: tcp
retries: 3 - host: 2222
container: 22
protocol: tcp
features: volumes:
- Git repositories with web UI - type: bind
- Built-in container/package registry source: /var/lib/archipelago/gitea/data
- Issue tracking and pull requests target: /data
- CI/CD via Gitea Actions options: [rw]
- Lightweight (SQLite, no external DB needed) - type: bind
source: /var/lib/archipelago/gitea/config
target: /etc/gitea
options: [rw]
environment:
- GITEA__database__DB_TYPE=sqlite3
- GITEA__server__SSH_PORT=2222
- GITEA__server__SSH_LISTEN_PORT=22
- GITEA__server__LFS_START_SERVER=true
- GITEA__packages__ENABLED=true
- GITEA__repository__ENABLE_PUSH_CREATE_USER=true
- GITEA__repository__ENABLE_PUSH_CREATE_ORG=true
health_check:
type: http
endpoint: http://localhost:3000
path: /
interval: 120s
timeout: 30s
retries: 5
interfaces:
main:
name: Web UI
description: Gitea web interface
type: ui
port: 3001
protocol: http
path: /
metadata:
icon: /assets/img/app-icons/gitea.svg
repo: https://gitea.com
tier: optional
launch:
open_in_new_tab: true
features:
- Git repositories with web UI
- Built-in container/package registry
- Issue tracking and pull requests
- CI/CD via Gitea Actions
- Lightweight SQLite deployment
nginx_proxy:
listen: 3000
proxy_pass: http://127.0.0.1:3001
extra_headers:
- proxy_hide_header X-Frame-Options
- proxy_hide_header Content-Security-Policy

View File

@ -49,5 +49,9 @@ app:
endpoint: http://localhost:3000 endpoint: http://localhost:3000
path: /api/health path: /api/health
interval: 30s interval: 30s
timeout: 5s timeout: 30s
retries: 3 retries: 5
metadata:
launch:
open_in_new_tab: true

View File

@ -1,29 +1,29 @@
app: app:
id: home-assistant id: homeassistant
name: Home Assistant name: Home Assistant
version: 2024.1.0 version: 2024.1.0
description: Open source home automation platform. Control and monitor your smart home devices. description: Open source home automation platform. Control and monitor your smart home devices.
container: container:
image: homeassistant/home-assistant:2024.1 image: 146.59.87.168:3000/lfg2025/home-assistant:2024.1
image_signature: cosign://...
pull_policy: if-not-present pull_policy: if-not-present
network: pasta
dependencies: dependencies:
- storage: 10Gi - storage: 10Gi
resources: resources:
cpu_limit: 2 cpu_limit: 2
memory_limit: 2Gi memory_limit: 512Mi
disk_limit: 10Gi disk_limit: 10Gi
security: security:
capabilities: [NET_BIND_SERVICE] capabilities: [CHOWN, FOWNER, SETUID, SETGID, DAC_OVERRIDE, NET_BIND_SERVICE, NET_RAW]
readonly_root: false # Home Assistant needs write access readonly_root: false # Home Assistant needs write access
no_new_privileges: true no_new_privileges: true
user: 1000 user: 1000
seccomp_profile: default seccomp_profile: default
network_policy: host # Requires host network for device discovery network_policy: isolated
apparmor_profile: home-assistant apparmor_profile: home-assistant
ports: ports:
@ -36,24 +36,23 @@ app:
source: /var/lib/archipelago/home-assistant source: /var/lib/archipelago/home-assistant
target: /config target: /config
options: [rw] options: [rw]
- type: bind
source: /var/run/dbus
target: /var/run/dbus
options: [ro]
devices: devices: []
- /dev/ttyUSB0 # Serial devices
- /dev/ttyACM0 # USB devices
environment: environment:
- TZ=UTC - TZ=UTC
- PUID=1000
- PGID=1000
health_check: health_check:
type: http type: tcp
endpoint: http://localhost:8123 endpoint: localhost:8123
path: /
interval: 30s interval: 30s
timeout: 5s timeout: 5s
retries: 3 retries: 3
metadata:
icon: /assets/img/app-icons/homeassistant.png
category: home
author: Home Assistant
repo: https://github.com/home-assistant/core
launch:
open_in_new_tab: true

View File

@ -1,12 +1,12 @@
app: app:
id: indeedhub id: indeedhub
name: Indeehub name: IndeeHub
version: 0.1.0 version: 1.0.0
description: Bitcoin documentary streaming platform featuring God Bless Bitcoin and other educational content about Bitcoin, sovereignty, and decentralized technology. Sign in with your Nostr identity. description: Bitcoin documentary streaming platform featuring God Bless Bitcoin and other educational content about Bitcoin, sovereignty, and decentralized technology. Sign in with your Nostr identity.
category: media category: community
container: container:
image: 146.59.87.168:3000/lfg2025/indeedhub:latest image: 146.59.87.168:3000/lfg2025/indeedhub:1.0.0
pull_policy: always # Pull from registry; falls back to local build pull_policy: always # Pull from registry; falls back to local build
network: indeedhub-net network: indeedhub-net
@ -70,8 +70,9 @@ app:
metadata: metadata:
author: Indeehub Team author: Indeehub Team
icon: /assets/img/app-icons/indeedhub.png
website: https://indeedhub.com website: https://indeedhub.com
source: https://github.com/indeedhub/indeedhub repo: https://github.com/indeedhub/indeedhub
license: MIT license: MIT
tags: tags:
- bitcoin - bitcoin

View File

@ -0,0 +1,52 @@
app:
id: jellyfin
name: Jellyfin
version: 10.8.13
description: Free media server. Stream movies, music, and photos.
container:
image: 146.59.87.168:3000/lfg2025/jellyfin:10.8.13
pull_policy: if-not-present
network: pasta
dependencies:
- storage: 10Gi
resources:
memory_limit: 1Gi
disk_limit: 10Gi
security:
capabilities: [CHOWN, FOWNER, SETUID, SETGID, DAC_OVERRIDE]
readonly_root: false
network_policy: isolated
ports:
- host: 8096
container: 8096
protocol: tcp
volumes:
- type: bind
source: /var/lib/archipelago/jellyfin/config
target: /config
options: [rw]
- type: bind
source: /var/lib/archipelago/jellyfin/cache
target: /cache
options: [rw]
environment: []
health_check:
type: tcp
endpoint: localhost:8096
interval: 30s
timeout: 5s
retries: 3
metadata:
icon: /assets/img/app-icons/jellyfin.webp
category: data
author: Jellyfin
repo: https://github.com/jellyfin/jellyfin

View File

@ -1,11 +1,11 @@
app: app:
id: lnd id: lnd
name: Lightning Network Daemon name: LND
version: 0.18.4 version: 0.18.4
description: Lightning Network implementation by Lightning Labs. Enables instant, low-cost Bitcoin payments. description: Lightning Network implementation by Lightning Labs. Enables instant, low-cost Bitcoin payments.
container: container:
image: git.tx1138.com/lfg2025/lnd:v0.18.4-beta image: 146.59.87.168:3000/lfg2025/lnd:v0.18.4-beta
pull_policy: if-not-present pull_policy: if-not-present
network: archy-net network: archy-net
secret_env: secret_env:

View File

@ -1,11 +1,11 @@
app: app:
id: mempool id: mempool
name: Mempool name: Mempool Explorer
version: 2.5.0 version: 3.0.0
description: Bitcoin mempool and blockchain explorer. Real-time transaction and block visualization. description: Bitcoin mempool and blockchain explorer. Real-time transaction and block visualization.
container: container:
image: mempool/mempool:v2.5.0 image: 146.59.87.168:3000/lfg2025/mempool-frontend:v3.0.0
image_signature: cosign://... image_signature: cosign://...
pull_policy: if-not-present pull_policy: if-not-present

View File

@ -1,13 +1,12 @@
app: app:
id: meshtastic id: meshtastic
name: Meshtastic name: Meshtastic
version: 2.5.0 version: 2-daily-alpine
description: Open-source mesh networking for LoRa radios. Create decentralized communication networks. description: Open-source mesh networking for LoRa radios. Create decentralized communication networks.
container: container:
image: meshtastic/meshtasticd:2.5.6 image: docker.io/meshtastic/meshtasticd:daily-alpine
image_signature: cosign://... pull_policy: if-not-present
pull_policy: verify-signature
dependencies: dependencies:
- storage: 1Gi - storage: 1Gi
@ -29,33 +28,42 @@ app:
ports: ports:
- host: 4403 - host: 4403
container: 4403 container: 4403
protocol: tcp # HTTP API protocol: tcp # Meshtastic TCP API
- host: 1883
container: 1883
protocol: tcp # MQTT (optional)
devices: devices:
- /dev/ttyUSB0 # LoRa radio device (if connected) - /dev/ttyUSB0 # LoRa radio device (if connected)
- /dev/ttyACM0 # Alternative device path
volumes: volumes:
- type: bind - type: bind
source: /var/lib/archipelago/meshtastic source: /var/lib/archipelago/meshtastic
target: /app/data target: /var/lib/meshtasticd
options: [rw] options: [rw]
files:
- path: /var/lib/archipelago/meshtastic/config.yaml
content: |
General:
MACAddress: AA:BB:CC:DD:EE:01
Webserver:
Port: 4403
environment: environment:
- MESHTASTIC_PORT=/dev/ttyUSB0 - MESHTASTIC_PORT=/dev/ttyUSB0
- MESHTASTIC_SERIAL=true - MESHTASTIC_SERIAL=true
health_check: health_check:
type: http type: cmd
endpoint: http://localhost:4403 endpoint: test -f /var/lib/meshtasticd/config.yaml
path: /health
interval: 30s interval: 30s
timeout: 5s timeout: 30s
retries: 3 retries: 5
networking: networking:
mesh_enabled: true mesh_enabled: true
local_network_access: true local_network_access: true
metadata:
icon: /assets/img/app-icons/meshcore.svg
category: networking
tier: recommended
repo: https://github.com/meshtastic/firmware

View File

@ -0,0 +1,50 @@
app:
id: nextcloud
name: Nextcloud
version: "29"
description: Your own private cloud. File sync, calendars, contacts.
container:
image: 146.59.87.168:3000/lfg2025/nextcloud:29
pull_policy: if-not-present
network: pasta
dependencies:
- storage: 10Gi
resources:
memory_limit: 1Gi
disk_limit: 10Gi
security:
capabilities: [CHOWN, SETUID, SETGID, DAC_OVERRIDE, NET_BIND_SERVICE]
readonly_root: false
network_policy: isolated
ports:
- host: 8085
container: 80
protocol: tcp
volumes:
- type: bind
source: /var/lib/archipelago/nextcloud
target: /var/www/html
options: [rw]
environment: []
health_check:
type: tcp
endpoint: localhost:80
interval: 30s
timeout: 5s
retries: 3
metadata:
icon: /assets/img/app-icons/nextcloud.webp
category: data
author: Nextcloud
repo: https://github.com/nextcloud/server
launch:
open_in_new_tab: true

View File

@ -28,7 +28,7 @@ app:
apparmor_profile: nostr-relay apparmor_profile: nostr-relay
ports: ports:
- host: 8081 - host: 18081
container: 8080 container: 8080
protocol: tcp # HTTP/WebSocket protocol: tcp # HTTP/WebSocket
@ -49,8 +49,8 @@ app:
endpoint: http://localhost:8080 endpoint: http://localhost:8080
path: / path: /
interval: 30s interval: 30s
timeout: 5s timeout: 30s
retries: 3 retries: 5
nostr_integration: nostr_integration:
relay_type: public relay_type: public

View File

@ -48,3 +48,7 @@ app:
interval: 30s interval: 30s
timeout: 5s timeout: 5s
retries: 3 retries: 3
metadata:
launch:
open_in_new_tab: true

View File

@ -0,0 +1,51 @@
app:
id: photoprism
name: PhotoPrism
version: "240915"
description: AI-powered photo management with facial recognition.
container:
image: 146.59.87.168:3000/lfg2025/photoprism:240915
pull_policy: if-not-present
dependencies:
- storage: 10Gi
resources:
memory_limit: 1Gi
disk_limit: 10Gi
security:
capabilities: [CHOWN, SETUID, SETGID]
readonly_root: false
network_policy: isolated
ports:
- host: 2342
container: 2342
protocol: tcp
volumes:
- type: bind
source: /var/lib/archipelago/photoprism
target: /photoprism/storage
options: [rw]
environment:
- PHOTOPRISM_ADMIN_PASSWORD=archipelago
- PHOTOPRISM_DEFAULT_LOCALE=en
health_check:
type: tcp
endpoint: localhost:2342
interval: 60s
timeout: 5s
retries: 3
metadata:
icon: /assets/img/app-icons/photoprism.svg
category: data
author: PhotoPrism
repo: https://github.com/photoprism/photoprism
launch:
open_in_new_tab: true

View File

@ -0,0 +1,64 @@
app:
id: portainer
name: Portainer
version: 2.19.4
description: Container management web UI for the local Podman socket.
category: development
container:
image: 146.59.87.168:3000/lfg2025/portainer:latest
pull_policy: if-not-present
data_uid: "1000:1000"
dependencies:
- storage: 1Gi
resources:
memory_limit: 256Mi
disk_limit: 1Gi
security:
capabilities: [CHOWN, SETUID, SETGID, DAC_OVERRIDE]
readonly_root: false
no_new_privileges: true
network_policy: isolated
ports:
- host: 9000
container: 9000
protocol: tcp
volumes:
- type: bind
source: /var/lib/archipelago/portainer
target: /data
options: [rw]
- type: bind
source: /var/lib/archipelago/portainer/compose
target: /data/compose
options: [rw]
- type: bind
source: /run/user/1000/podman/podman.sock
target: /var/run/docker.sock
options: [rw]
environment: []
interfaces:
main:
name: Web UI
description: Portainer web interface
type: ui
port: 9000
protocol: http
path: /
metadata:
icon: /assets/img/app-icons/portainer.webp
tier: optional
launch:
open_in_new_tab: true
features:
- Container management dashboard
- Local Podman socket access
- Compose stack storage

View File

@ -45,5 +45,5 @@ app:
endpoint: http://localhost:8080 endpoint: http://localhost:8080
path: / path: /
interval: 30s interval: 30s
timeout: 5s timeout: 30s
retries: 3 retries: 5

View File

@ -0,0 +1,54 @@
app:
id: uptime-kuma
name: Uptime Kuma
version: 1.23.0
description: Self-hosted uptime monitoring.
container:
image: 146.59.87.168:3000/lfg2025/uptime-kuma:1
pull_policy: if-not-present
network: pasta
custom_args: ["--", "node", "server/server.js"]
dependencies:
- storage: 1Gi
resources:
memory_limit: 256Mi
disk_limit: 1Gi
security:
capabilities: [CHOWN, FOWNER, SETUID, SETGID]
readonly_root: false
network_policy: isolated
ports:
- host: 3002
container: 3001
protocol: tcp
volumes:
- type: bind
source: /var/lib/archipelago/uptime-kuma
target: /app/data
options: [rw]
environment:
- TZ=UTC
health_check:
type: http
endpoint: localhost:3001
path: /
interval: 30s
timeout: 5s
retries: 3
metadata:
icon: /assets/img/app-icons/uptime-kuma.webp
category: data
tier: recommended
author: Uptime Kuma
repo: https://github.com/louislam/uptime-kuma
launch:
open_in_new_tab: true

View File

@ -0,0 +1,51 @@
app:
id: vaultwarden
name: Vaultwarden
version: 1.30.0
description: Self-hosted password vault with zero-knowledge encryption.
container:
image: 146.59.87.168:3000/lfg2025/vaultwarden:1.30.0-alpine
pull_policy: if-not-present
network: pasta
dependencies:
- storage: 1Gi
resources:
memory_limit: 256Mi
disk_limit: 1Gi
security:
capabilities: [CHOWN, SETUID, SETGID, NET_BIND_SERVICE]
readonly_root: false
network_policy: isolated
ports:
- host: 8082
container: 80
protocol: tcp
volumes:
- type: bind
source: /var/lib/archipelago/vaultwarden
target: /data
options: [rw]
environment: []
health_check:
type: tcp
endpoint: localhost:80
interval: 30s
timeout: 5s
retries: 3
metadata:
icon: /assets/img/app-icons/vaultwarden.webp
category: data
tier: recommended
author: Vaultwarden
repo: https://github.com/dani-garcia/vaultwarden
launch:
open_in_new_tab: true

View File

@ -0,0 +1,3 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.9 21.8V15.8H24.9V21.8H18.9ZM25.5 21.8V15.8H31.6V21.8H25.5ZM32.2 21.8V15.8H38.3V21.8H32.2ZM39 21.8V15.8H44.9V21.8H39ZM39 28.5V22.5H44.9V28.5H39ZM45.6 28.5V22.5H51.7V28.5H45.6ZM12.2 35.2V29.2H18.3V35.2H12.2ZM18.9 35.2V29.2H24.9V35.2H18.9ZM25.5 35.2V29.2H31.6V35.2H25.5ZM32.2 35.2V29.2H38.3V35.2H32.2ZM39 35.2V29.2H44.9V35.2H39ZM45.6 35.2V29.2H51.7V35.2H45.6ZM12.2 41.8V35.8H18.3V41.8H12.2ZM18.9 41.8V35.8H24.9V41.8H18.9ZM39 41.8V35.8H44.9V41.8H39ZM45.6 41.8V35.8H51.7V41.8H45.6ZM18.9 48.5V42.5H24.9V48.5H18.9ZM25.5 48.5V42.5H31.6V48.5H25.5ZM32.2 48.5V42.5H38.3V48.5H32.2ZM39 48.5V42.5H44.9V48.5H39Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 731 B

View File

@ -0,0 +1,28 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-labelledby="title desc">
<title id="title">Meshtastic</title>
<desc id="desc">LoRa mesh radio nodes connected by signal links</desc>
<defs>
<linearGradient id="radioBody" x1="27" y1="28" x2="93" y2="107" gradientUnits="userSpaceOnUse">
<stop stop-color="#f8fafc"/>
<stop offset="1" stop-color="#94a3b8"/>
</linearGradient>
<linearGradient id="signal" x1="16" y1="14" x2="112" y2="114" gradientUnits="userSpaceOnUse">
<stop stop-color="#38bdf8"/>
<stop offset="0.52" stop-color="#22c55e"/>
<stop offset="1" stop-color="#f59e0b"/>
</linearGradient>
</defs>
<rect width="128" height="128" rx="26" fill="#0f172a"/>
<path d="M36 31 50 78" stroke="#64748b" stroke-width="6" stroke-linecap="round"/>
<path d="M32 28c7-5 16-5 23 0" stroke="#38bdf8" stroke-width="5" stroke-linecap="round" fill="none"/>
<path d="M24 18c12-9 28-9 40 0" stroke="#38bdf8" stroke-opacity=".55" stroke-width="5" stroke-linecap="round" fill="none"/>
<rect x="42" y="54" width="50" height="55" rx="11" fill="url(#radioBody)"/>
<rect x="51" y="64" width="32" height="15" rx="4" fill="#0f172a"/>
<circle cx="57" cy="94" r="5" fill="#0f172a"/>
<circle cx="77" cy="94" r="5" fill="#0f172a"/>
<circle cx="26" cy="84" r="8" fill="#38bdf8"/>
<circle cx="102" cy="43" r="8" fill="#22c55e"/>
<circle cx="103" cy="99" r="8" fill="#f59e0b"/>
<path d="M33 81 51 72M82 65l15-17M88 93l8 3" stroke="url(#signal)" stroke-width="5" stroke-linecap="round"/>
<path d="M22 103c25-16 55-16 84 0" stroke="#22c55e" stroke-opacity=".32" stroke-width="5" stroke-linecap="round" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,5 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-label="Saleor">
<rect width="128" height="128" rx="30" fill="#111827"/>
<path d="M34 42c0-10 9-18 22-18h38v16H56c-5 0-8 2-8 5 0 4 4 5 13 7l15 3c15 3 24 11 24 24 0 15-12 25-31 25H31V88h39c8 0 13-3 13-8 0-4-4-6-12-8l-16-3C41 66 34 57 34 42Z" fill="#fff"/>
<path d="M29 103h70v8H29z" fill="#7C3AED"/>
</svg>

Before

Width:  |  Height:  |  Size: 389 B

View File

@ -14,7 +14,7 @@
"id": "bitcoin-knots", "id": "bitcoin-knots",
"title": "Bitcoin Knots", "title": "Bitcoin Knots",
"version": "28.1.0", "version": "28.1.0",
"description": "Run a full Bitcoin node. Validate and relay blocks and transactions.", "description": "Full Bitcoin Knots node with dynamic prune/full-mode startup based on host disk.",
"icon": "/assets/img/app-icons/bitcoin-knots.webp", "icon": "/assets/img/app-icons/bitcoin-knots.webp",
"author": "Bitcoin Knots", "author": "Bitcoin Knots",
"category": "money", "category": "money",
@ -25,8 +25,8 @@
{ {
"id": "bitcoin-core", "id": "bitcoin-core",
"title": "Bitcoin Core", "title": "Bitcoin Core",
"version": "28.4", "version": "28.4.0",
"description": "Reference Bitcoin node implementation. Alternative to Bitcoin Knots; uninstall Knots before switching.", "description": "Reference Bitcoin Core node with dynamic prune/full-mode startup based on host disk.",
"icon": "/assets/img/app-icons/bitcoin-core.svg", "icon": "/assets/img/app-icons/bitcoin-core.svg",
"author": "Bitcoin Core contributors", "author": "Bitcoin Core contributors",
"category": "money", "category": "money",
@ -38,7 +38,7 @@
"id": "lnd", "id": "lnd",
"title": "LND", "title": "LND",
"version": "0.18.4", "version": "0.18.4",
"description": "Lightning Network Daemon. Fast Bitcoin payments through Lightning.", "description": "Lightning Network implementation by Lightning Labs. Enables instant, low-cost Bitcoin payments.",
"icon": "/assets/img/app-icons/lnd.svg", "icon": "/assets/img/app-icons/lnd.svg",
"author": "Lightning Labs", "author": "Lightning Labs",
"category": "money", "category": "money",
@ -53,7 +53,7 @@
"id": "btcpay-server", "id": "btcpay-server",
"title": "BTCPay Server", "title": "BTCPay Server",
"version": "2.3.9", "version": "2.3.9",
"description": "Self-hosted Bitcoin payment processor.", "description": "Self-hosted Bitcoin payment processor. Accept Bitcoin payments without intermediaries.",
"icon": "/assets/img/app-icons/btcpay-server.png", "icon": "/assets/img/app-icons/btcpay-server.png",
"author": "BTCPay Server Foundation", "author": "BTCPay Server Foundation",
"category": "commerce", "category": "commerce",
@ -76,8 +76,17 @@
"dockerImage": "ghcr.io/saleor/saleor:3.23", "dockerImage": "ghcr.io/saleor/saleor:3.23",
"repoUrl": "https://github.com/saleor/saleor", "repoUrl": "https://github.com/saleor/saleor",
"containerConfig": { "containerConfig": {
"ports": ["9011:80", "9010:80", "8000:8000", "8025:8025", "16686:16686"], "ports": [
"volumes": ["/var/lib/archipelago/saleor:/app/media", "/var/lib/archipelago/saleor-db:/var/lib/postgresql/data"], "9011:80",
"9010:80",
"8000:8000",
"8025:8025",
"16686:16686"
],
"volumes": [
"/var/lib/archipelago/saleor:/app/media",
"/var/lib/archipelago/saleor-db:/var/lib/postgresql/data"
],
"notes": "Installed as a Saleor stack: customer storefront on 9011, admin dashboard on 9010, API on 8000, Mailpit on 8025, and Jaeger on 16686. Supporting containers include PostgreSQL, Valkey, Celery worker, and services required by Saleor." "notes": "Installed as a Saleor stack: customer storefront on 9011, admin dashboard on 9010, API on 8000, Mailpit on 8025, and Jaeger on 16686. Supporting containers include PostgreSQL, Valkey, Celery worker, and services required by Saleor."
} }
}, },
@ -85,7 +94,7 @@
"id": "mempool", "id": "mempool",
"title": "Mempool Explorer", "title": "Mempool Explorer",
"version": "3.0.0", "version": "3.0.0",
"description": "Self-hosted Bitcoin blockchain and mempool visualizer.", "description": "Bitcoin mempool and blockchain explorer. Real-time transaction and block visualization.",
"icon": "/assets/img/app-icons/mempool.webp", "icon": "/assets/img/app-icons/mempool.webp",
"author": "Mempool", "author": "Mempool",
"category": "money", "category": "money",
@ -101,7 +110,7 @@
"id": "electrumx", "id": "electrumx",
"title": "ElectrumX", "title": "ElectrumX",
"version": "1.18.0", "version": "1.18.0",
"description": "Electrum protocol server. Index the blockchain for fast wallet lookups.", "description": "Electrum server indexing Bitcoin chain data for lightweight wallet queries.",
"icon": "/assets/img/app-icons/electrumx.png", "icon": "/assets/img/app-icons/electrumx.png",
"author": "Luke Childs", "author": "Luke Childs",
"category": "money", "category": "money",
@ -116,7 +125,7 @@
"id": "indeedhub", "id": "indeedhub",
"title": "IndeeHub", "title": "IndeeHub",
"version": "1.0.0", "version": "1.0.0",
"description": "Bitcoin documentary streaming with Nostr identity.", "description": "Bitcoin documentary streaming platform featuring God Bless Bitcoin and other educational content about Bitcoin, sovereignty, and decentralized technology. Sign in with your Nostr identity.",
"icon": "/assets/img/app-icons/indeedhub.png", "icon": "/assets/img/app-icons/indeedhub.png",
"author": "IndeeHub", "author": "IndeeHub",
"category": "community", "category": "community",
@ -127,49 +136,133 @@
"id": "botfights", "id": "botfights",
"title": "BotFights", "title": "BotFights",
"version": "1.1.0", "version": "1.1.0",
"description": "Bot arena + 2-player arcade fighter with controller support and Adventure Mode.", "description": "Bot competition arena with 2-player arcade fighting mode. AI bots battle in trivia challenges while humans duke it out with controllers. Built for Bitcoiners.",
"icon": "/assets/img/app-icons/botfights.svg", "icon": "/assets/img/app-icons/botfights.svg",
"author": "BotFights", "author": "BotFights",
"category": "community", "category": "community",
"dockerImage": "146.59.87.168:3000/lfg2025/botfights:1.1.0", "dockerImage": "146.59.87.168:3000/lfg2025/botfights:1.1.0",
"repoUrl": "https://botfights.net", "repoUrl": "https://botfights.net",
"containerConfig": { "containerConfig": {
"ports": ["9100:9100"], "ports": [
"volumes": ["/var/lib/archipelago/botfights:/app/server/data"], "9100:9100"
"env": ["NODE_ENV=production", "PORT=9100", "FIGHT_LOOP_ENABLED=true", "ARCHY_EMBEDDED=1"] ],
"volumes": [
"/var/lib/archipelago/botfights:/app/server/data"
],
"env": [
"NODE_ENV=production",
"PORT=9100",
"FIGHT_LOOP_ENABLED=true",
"ARCHY_EMBEDDED=1"
]
} }
}, },
{ {
"id": "gitea", "id": "gitea",
"title": "Gitea", "title": "Gitea",
"version": "1.23", "version": "1.23",
"description": "Self-hosted Git service with container registry, CI/CD, issue tracking.", "description": "Self-hosted Git service with built-in container registry, CI/CD, and package hosting.",
"icon": "/assets/img/app-icons/gitea.svg", "icon": "/assets/img/app-icons/gitea.svg",
"author": "Gitea", "author": "Gitea",
"category": "development", "category": "development",
"dockerImage": "146.59.87.168:3000/lfg2025/gitea:1.23", "dockerImage": "docker.io/gitea/gitea:1.23",
"repoUrl": "https://gitea.com", "repoUrl": "https://gitea.com",
"containerConfig": { "containerConfig": {
"ports": ["3001:3000", "2222:22"], "ports": [
"volumes": ["/var/lib/archipelago/gitea/data:/data", "/var/lib/archipelago/gitea/config:/etc/gitea"], "3001:3000",
"env": ["GITEA__database__DB_TYPE=sqlite3", "GITEA__server__SSH_PORT=2222", "GITEA__server__SSH_LISTEN_PORT=22", "GITEA__server__LFS_START_SERVER=true", "GITEA__packages__ENABLED=true", "GITEA__repository__ENABLE_PUSH_CREATE_USER=true", "GITEA__repository__ENABLE_PUSH_CREATE_ORG=true", "GITEA__security__X_FRAME_OPTIONS="] "2222:22"
} ],
"volumes": [
"/var/lib/archipelago/gitea/data:/data",
"/var/lib/archipelago/gitea/config:/etc/gitea"
],
"env": [
"GITEA__database__DB_TYPE=sqlite3",
"GITEA__server__SSH_PORT=2222",
"GITEA__server__SSH_LISTEN_PORT=22",
"GITEA__server__LFS_START_SERVER=true",
"GITEA__packages__ENABLED=true",
"GITEA__repository__ENABLE_PUSH_CREATE_USER=true",
"GITEA__repository__ENABLE_PUSH_CREATE_ORG=true",
"GITEA__security__X_FRAME_OPTIONS="
]
},
"tier": "optional"
}, },
{ {
"id": "filebrowser", "id": "filebrowser",
"title": "File Browser", "title": "File Browser",
"version": "2.27.0", "version": "2.27.0",
"description": "Web-based file manager.", "description": "Baseline Archipelago file manager service.",
"icon": "/assets/img/app-icons/file-browser.webp", "icon": "/assets/img/app-icons/file-browser.webp",
"author": "File Browser", "author": "File Browser",
"category": "data", "category": "data",
"tier": "core", "tier": "core",
"dockerImage": "146.59.87.168:3000/lfg2025/filebrowser:v2.27.0", "dockerImage": "git.tx1138.com/lfg2025/filebrowser:v2.27.0",
"repoUrl": "https://github.com/filebrowser/filebrowser", "repoUrl": "https://github.com/filebrowser/filebrowser",
"containerConfig": { "containerConfig": {
"ports": ["8083:80"], "ports": [
"volumes": ["/var/lib/archipelago/filebrowser:/srv", "/var/lib/archipelago/filebrowser-data:/data"], "8083:80"
"args": ["--database=/data/database.db", "--root=/srv", "--address=0.0.0.0", "--port=80"] ],
"volumes": [
"/var/lib/archipelago/filebrowser:/srv",
"/var/lib/archipelago/filebrowser-data:/data"
],
"args": [
"--database=/data/database.db",
"--root=/srv",
"--address=0.0.0.0",
"--port=80"
]
}
},
{
"id": "nostr-rs-relay",
"title": "Nostr Relay (Rust)",
"version": "0.8.0",
"description": "High-performance Nostr relay written in Rust. Host your own decentralized social media relay and earn networking profits.",
"icon": "/assets/img/app-icons/nostr.svg",
"author": "Nostr RS Relay",
"category": "community",
"tier": "recommended",
"dockerImage": "scsibug/nostr-rs-relay:0.8.9",
"repoUrl": "https://github.com/scsibug/nostr-rs-relay",
"containerConfig": {
"ports": [
"8081:8080"
],
"volumes": [
"/var/lib/archipelago/nostr-relay:/usr/src/app/db"
],
"env": [
"RELAY_NAME=Archipelago Nostr Relay",
"RELAY_DESCRIPTION=Self-hosted Nostr relay on Archipelago"
]
}
},
{
"id": "meshtastic",
"title": "Meshtastic",
"version": "2-daily-alpine",
"description": "Open-source mesh networking for LoRa radios. Create decentralized communication networks.",
"icon": "/assets/img/app-icons/meshcore.svg",
"author": "Meshtastic",
"category": "networking",
"tier": "recommended",
"dockerImage": "docker.io/meshtastic/meshtasticd:daily-alpine",
"repoUrl": "https://github.com/meshtastic/firmware",
"containerConfig": {
"ports": [
"4403:4403"
],
"volumes": [
"/var/lib/archipelago/meshtastic:/var/lib/meshtasticd"
],
"env": [
"MESHTASTIC_PORT=/dev/ttyUSB0",
"MESHTASTIC_SERIAL=true"
],
"notes": "Requires a LoRa radio device at /dev/ttyUSB0. The config file is rendered from the app manifest before container start."
} }
}, },
{ {
@ -184,15 +277,19 @@
"dockerImage": "146.59.87.168:3000/lfg2025/vaultwarden:1.30.0-alpine", "dockerImage": "146.59.87.168:3000/lfg2025/vaultwarden:1.30.0-alpine",
"repoUrl": "https://github.com/dani-garcia/vaultwarden", "repoUrl": "https://github.com/dani-garcia/vaultwarden",
"containerConfig": { "containerConfig": {
"ports": ["8082:80"], "ports": [
"volumes": ["/var/lib/archipelago/vaultwarden:/data"] "8082:80"
],
"volumes": [
"/var/lib/archipelago/vaultwarden:/data"
]
} }
}, },
{ {
"id": "searxng", "id": "searxng",
"title": "SearXNG", "title": "SearXNG",
"version": "2024.1.0", "version": "1.0.0",
"description": "Privacy-respecting metasearch engine.", "description": "Privacy-respecting metasearch engine. Search the web without tracking.",
"icon": "/assets/img/app-icons/searxng.png", "icon": "/assets/img/app-icons/searxng.png",
"author": "SearXNG", "author": "SearXNG",
"category": "data", "category": "data",
@ -200,21 +297,46 @@
"dockerImage": "146.59.87.168:3000/lfg2025/searxng:latest", "dockerImage": "146.59.87.168:3000/lfg2025/searxng:latest",
"repoUrl": "https://github.com/searxng/searxng", "repoUrl": "https://github.com/searxng/searxng",
"containerConfig": { "containerConfig": {
"ports": ["8888:8080"], "ports": [
"volumes": ["/var/lib/archipelago/searxng:/etc/searxng"] "8888:8080"
],
"volumes": [
"/var/lib/archipelago/searxng:/etc/searxng"
]
} }
}, },
{ {
"id": "fedimint", "id": "fedimint",
"title": "Fedimint", "title": "Fedimint",
"version": "0.10.0", "version": "0.10.0",
"description": "Federated Bitcoin mint with privacy through federated guardians.", "description": "Federated Bitcoin minting service with built-in Guardian UI. Privacy-preserving Bitcoin custody.",
"icon": "/assets/img/app-icons/fedimint.png", "icon": "/assets/img/app-icons/fedimint.png",
"author": "Fedimint", "author": "Fedimint",
"category": "money", "category": "money",
"dockerImage": "146.59.87.168:3000/lfg2025/fedimintd:v0.10.0", "dockerImage": "146.59.87.168:3000/lfg2025/fedimintd:v0.10.0",
"repoUrl": "https://github.com/fedimint/fedimint" "repoUrl": "https://github.com/fedimint/fedimint"
}, },
{
"id": "fedimint-gateway",
"title": "Fedimint Gateway",
"version": "0.10.0",
"description": "Fedimint gateway service with automatic LND-or-LDK backend selection.",
"icon": "/assets/img/app-icons/fedimint.png",
"author": "Fedimint",
"category": "money",
"dockerImage": "git.tx1138.com/lfg2025/gatewayd:v0.10.0",
"repoUrl": "https://github.com/fedimint/fedimint",
"containerConfig": {
"ports": [
"8176:8176",
"9737:9737"
],
"volumes": [
"/var/lib/archipelago/fedimint-gateway:/data",
"/var/lib/archipelago/lnd:/lnd:ro"
]
}
},
{ {
"id": "jellyfin", "id": "jellyfin",
"title": "Jellyfin", "title": "Jellyfin",
@ -226,8 +348,13 @@
"dockerImage": "146.59.87.168:3000/lfg2025/jellyfin:10.8.13", "dockerImage": "146.59.87.168:3000/lfg2025/jellyfin:10.8.13",
"repoUrl": "https://github.com/jellyfin/jellyfin", "repoUrl": "https://github.com/jellyfin/jellyfin",
"containerConfig": { "containerConfig": {
"ports": ["8096:8096"], "ports": [
"volumes": ["/var/lib/archipelago/jellyfin/config:/config", "/var/lib/archipelago/jellyfin/cache:/cache"] "8096:8096"
],
"volumes": [
"/var/lib/archipelago/jellyfin/config:/config",
"/var/lib/archipelago/jellyfin/cache:/cache"
]
} }
}, },
{ {
@ -244,34 +371,47 @@
{ {
"id": "homeassistant", "id": "homeassistant",
"title": "Home Assistant", "title": "Home Assistant",
"version": "2024.1", "version": "2024.1.0",
"description": "Open-source home automation.", "description": "Open source home automation platform. Control and monitor your smart home devices.",
"icon": "/assets/img/app-icons/homeassistant.png", "icon": "/assets/img/app-icons/homeassistant.png",
"author": "Home Assistant", "author": "Home Assistant",
"category": "home", "category": "home",
"dockerImage": "146.59.87.168:3000/lfg2025/home-assistant:2024.1", "dockerImage": "146.59.87.168:3000/lfg2025/home-assistant:2024.1",
"repoUrl": "https://github.com/home-assistant/core", "repoUrl": "https://github.com/home-assistant/core",
"containerConfig": { "containerConfig": {
"ports": ["8123:8123"], "ports": [
"volumes": ["/var/lib/archipelago/home-assistant:/config"], "8123:8123"
"env": ["TZ=UTC"] ],
"volumes": [
"/var/lib/archipelago/home-assistant:/config"
],
"env": [
"TZ=UTC"
]
} }
}, },
{ {
"id": "grafana", "id": "grafana",
"title": "Grafana", "title": "Grafana",
"version": "10.2.0", "version": "10.2.0",
"description": "Analytics and monitoring dashboards.", "description": "Analytics and monitoring platform. Visualize metrics and create dashboards.",
"icon": "/assets/img/app-icons/grafana.png", "icon": "/assets/img/app-icons/grafana.png",
"author": "Grafana Labs", "author": "Grafana Labs",
"category": "data", "category": "data",
"tier": "recommended", "tier": "recommended",
"dockerImage": "146.59.87.168:3000/lfg2025/grafana:10.2.0", "dockerImage": "grafana/grafana:10.2.0",
"repoUrl": "https://github.com/grafana/grafana", "repoUrl": "https://github.com/grafana/grafana",
"containerConfig": { "containerConfig": {
"ports": ["3000:3000"], "ports": [
"volumes": ["/var/lib/archipelago/grafana:/var/lib/grafana"], "3000:3000"
"env": ["GF_PATHS_DATA=/var/lib/grafana", "GF_USERS_ALLOW_SIGN_UP=false"] ],
"volumes": [
"/var/lib/archipelago/grafana:/var/lib/grafana"
],
"env": [
"GF_PATHS_DATA=/var/lib/grafana",
"GF_USERS_ALLOW_SIGN_UP=false"
]
} }
}, },
{ {
@ -286,10 +426,42 @@
"dockerImage": "146.59.87.168:3000/lfg2025/tailscale:stable", "dockerImage": "146.59.87.168:3000/lfg2025/tailscale:stable",
"repoUrl": "https://github.com/tailscale/tailscale", "repoUrl": "https://github.com/tailscale/tailscale",
"containerConfig": { "containerConfig": {
"ports": ["8240:8240"], "ports": [
"volumes": ["/var/lib/archipelago/tailscale:/var/lib/tailscale"], "8240:8240"
"env": ["TS_STATE_DIR=/var/lib/tailscale"], ],
"args": ["sh", "-c", "tailscaled --tun=userspace-networking & sleep 2; tailscale web --listen 0.0.0.0:8240 & wait"] "volumes": [
"/var/lib/archipelago/tailscale:/var/lib/tailscale"
],
"env": [
"TS_STATE_DIR=/var/lib/tailscale"
],
"args": [
"sh",
"-c",
"tailscaled --tun=userspace-networking & for i in $(seq 1 30); do [ -S /var/run/tailscale/tailscaled.sock ] && break; sleep 1; done; tailscale web --listen 0.0.0.0:8240 & wait"
]
}
},
{
"id": "portainer",
"title": "Portainer",
"version": "2.19.4",
"description": "Container management web UI for the local Podman socket.",
"icon": "/assets/img/app-icons/portainer.webp",
"author": "Portainer",
"category": "development",
"tier": "optional",
"dockerImage": "146.59.87.168:3000/lfg2025/portainer:latest",
"repoUrl": "https://github.com/portainer/portainer",
"containerConfig": {
"ports": [
"9000:9000"
],
"volumes": [
"/var/lib/archipelago/portainer:/data",
"/run/user/1000/podman/podman.sock:/var/run/docker.sock"
],
"notes": "Uses the manifest-owned Podman socket bind mount preparation path."
} }
}, },
{ {
@ -304,8 +476,14 @@
"dockerImage": "docker.io/netbirdio/dashboard:v2.38.0", "dockerImage": "docker.io/netbirdio/dashboard:v2.38.0",
"repoUrl": "https://github.com/netbirdio/netbird", "repoUrl": "https://github.com/netbirdio/netbird",
"containerConfig": { "containerConfig": {
"ports": ["8087:80", "8086:80", "3478:3478/udp"], "ports": [
"volumes": ["/var/lib/archipelago/netbird:/var/lib/netbird"], "8087:80",
"8086:80",
"3478:3478/udp"
],
"volumes": [
"/var/lib/archipelago/netbird:/var/lib/netbird"
],
"notes": "Installed as a two-container stack: netbird dashboard on 8087 and netbird-server control plane on 8086 plus UDP 3478. For production clients, publish a DNS name over HTTPS with gRPC/WebSocket routing." "notes": "Installed as a two-container stack: netbird dashboard on 8087 and netbird-server control plane on 8086 plus UDP 3478. For production clients, publish a DNS name over HTTPS with gRPC/WebSocket routing."
} }
}, },
@ -321,10 +499,20 @@
"dockerImage": "146.59.87.168:3000/lfg2025/uptime-kuma:1", "dockerImage": "146.59.87.168:3000/lfg2025/uptime-kuma:1",
"repoUrl": "https://github.com/louislam/uptime-kuma", "repoUrl": "https://github.com/louislam/uptime-kuma",
"containerConfig": { "containerConfig": {
"ports": ["3002:3001"], "ports": [
"volumes": ["/var/lib/archipelago/uptime-kuma:/app/data"], "3002:3001"
"env": ["TZ=UTC"], ],
"args": ["--", "node", "server/server.js"] "volumes": [
"/var/lib/archipelago/uptime-kuma:/app/data"
],
"env": [
"TZ=UTC"
],
"args": [
"--",
"node",
"server/server.js"
]
} }
}, },
{ {
@ -338,24 +526,35 @@
"dockerImage": "146.59.87.168:3000/lfg2025/photoprism:240915", "dockerImage": "146.59.87.168:3000/lfg2025/photoprism:240915",
"repoUrl": "https://github.com/photoprism/photoprism", "repoUrl": "https://github.com/photoprism/photoprism",
"containerConfig": { "containerConfig": {
"ports": ["2342:2342"], "ports": [
"volumes": ["/var/lib/archipelago/photoprism:/photoprism/storage"], "2342:2342"
"env": ["PHOTOPRISM_ADMIN_PASSWORD=archipelago", "PHOTOPRISM_DEFAULT_LOCALE=en"] ],
"volumes": [
"/var/lib/archipelago/photoprism:/photoprism/storage"
],
"env": [
"PHOTOPRISM_ADMIN_PASSWORD=archipelago",
"PHOTOPRISM_DEFAULT_LOCALE=en"
]
} }
}, },
{ {
"id": "nextcloud", "id": "nextcloud",
"title": "Nextcloud", "title": "Nextcloud",
"version": "28", "version": "29",
"description": "Your own private cloud. File sync, calendars, contacts.", "description": "Your own private cloud. File sync, calendars, contacts.",
"icon": "/assets/img/app-icons/nextcloud.webp", "icon": "/assets/img/app-icons/nextcloud.webp",
"author": "Nextcloud", "author": "Nextcloud",
"category": "data", "category": "data",
"dockerImage": "146.59.87.168:3000/lfg2025/nextcloud:28", "dockerImage": "146.59.87.168:3000/lfg2025/nextcloud:29",
"repoUrl": "https://github.com/nextcloud/server", "repoUrl": "https://github.com/nextcloud/server",
"containerConfig": { "containerConfig": {
"ports": ["8085:80"], "ports": [
"volumes": ["/var/lib/archipelago/nextcloud:/var/www/html"] "8085:80"
],
"volumes": [
"/var/lib/archipelago/nextcloud:/var/www/html"
]
} }
} }
] ]

View File

@ -0,0 +1,90 @@
/** Generated by scripts/generate-app-catalog.py. Do not edit manually. */
export const GENERATED_APP_PORTS: Record<string, number> = {
"aiui": 5180,
"archy-mempool-web": 4080,
"archy-nbxplorer": 32838,
"botfights": 9100,
"btcpay-server": 23000,
"did-wallet": 8083,
"electrumx": 50001,
"fedimint": 8175,
"filebrowser": 8083,
"gitea": 3001,
"grafana": 3000,
"homeassistant": 8123,
"indeedhub": 7778,
"jellyfin": 8096,
"lnd-ui": 18083,
"mempool": 4080,
"mempool-api": 8999,
"meshtastic": 4403,
"morphos-server": 8086,
"nextcloud": 8085,
"nostr-rs-relay": 18081,
"onlyoffice": 8088,
"photoprism": 2342,
"portainer": 9000,
"router": 8084,
"searxng": 8888,
"strfry": 8082,
"uptime-kuma": 3002,
"vaultwarden": 8082,
"web5-dwn": 3000,
}
export const GENERATED_APP_TITLES: Record<string, string> = {
"aiui": "AI Assistant",
"archy-btcpay-db": "BTCPay Postgres",
"archy-mempool-db": "Mempool MariaDB",
"archy-mempool-web": "Mempool Web",
"archy-nbxplorer": "NBXplorer",
"bitcoin-core": "Bitcoin Core",
"bitcoin-knots": "Bitcoin Knots",
"bitcoin-ui": "Bitcoin UI",
"botfights": "BotFights",
"btcpay-server": "BTCPay Server",
"core-lightning": "Core Lightning (CLN)",
"did-wallet": "Web5 DID Wallet",
"electrs-ui": "Electrs UI",
"electrumx": "ElectrumX",
"fedimint": "Fedimint",
"fedimint-gateway": "Fedimint Gateway",
"filebrowser": "File Browser",
"gitea": "Gitea",
"grafana": "Grafana",
"homeassistant": "Home Assistant",
"indeedhub": "IndeeHub",
"jellyfin": "Jellyfin",
"lightning-stack": "Lightning Stack",
"lnd": "LND",
"lnd-ui": "LND UI",
"mempool": "Mempool Explorer",
"mempool-api": "Mempool API",
"meshtastic": "Meshtastic",
"morphos-server": "MorphOS Server",
"nextcloud": "Nextcloud",
"nostr-rs-relay": "Nostr Relay (Rust)",
"onlyoffice": "OnlyOffice",
"photoprism": "PhotoPrism",
"portainer": "Portainer",
"router": "Mesh Router",
"searxng": "SearXNG",
"strfry": "Strfry Nostr Relay",
"uptime-kuma": "Uptime Kuma",
"vaultwarden": "Vaultwarden",
"web5-dwn": "Decentralized Web Node",
}
export const GENERATED_NEW_TAB_APPS = new Set<string>([
"btcpay-server",
"gitea",
"grafana",
"homeassistant",
"nextcloud",
"onlyoffice",
"photoprism",
"portainer",
"uptime-kuma",
"vaultwarden",
])

View File

@ -0,0 +1,173 @@
#!/usr/bin/env python3
"""Report drift between app-catalog/catalog.json and apps/*/manifest.yml."""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
import yaml
INTERNAL_MANIFEST_IDS = {
"aiui",
"archy-btcpay-db",
"archy-mempool-db",
"archy-mempool-web",
"archy-nbxplorer",
"bitcoin-ui",
"core-lightning",
"did-wallet",
"electrs-ui",
"lightning-stack",
"lnd-ui",
"mempool-api",
"morphos-server",
"onlyoffice",
"router",
"strfry",
"web5-dwn",
}
LEGACY_STACK_CATALOG_IDS = {
"immich",
"netbird",
"saleor",
"tailscale",
}
def load_catalog(path: Path) -> dict[str, dict[str, Any]]:
with path.open("r", encoding="utf-8") as fh:
data = json.load(fh)
apps = data.get("apps", [])
if not isinstance(apps, list):
raise ValueError(f"{path}: expected .apps to be a list")
return {str(app.get("id", "")): app for app in apps if isinstance(app, dict) and app.get("id")}
def load_manifests(apps_dir: Path) -> dict[str, dict[str, Any]]:
manifests: dict[str, dict[str, Any]] = {}
for path in sorted(apps_dir.glob("*/manifest.yml")):
with path.open("r", encoding="utf-8") as fh:
data = yaml.safe_load(fh)
if not isinstance(data, dict) or not isinstance(data.get("app"), dict):
continue
app = data["app"]
app_id = app.get("id")
if app_id:
manifests[str(app_id)] = {"path": str(path), "app": app}
return manifests
def metadata(app: dict[str, Any]) -> dict[str, Any]:
value = app.get("metadata")
return value if isinstance(value, dict) else {}
def manifest_value(app: dict[str, Any], field: str) -> Any:
meta = metadata(app)
container = app.get("container") if isinstance(app.get("container"), dict) else {}
match field:
case "title":
return app.get("name")
case "version":
return str(app.get("version", ""))
case "description":
return app.get("description")
case "dockerImage":
return container.get("image")
case "category":
return app.get("category") or meta.get("category")
case "tier":
return meta.get("tier")
case "icon":
return meta.get("icon")
case "repoUrl":
return meta.get("repo") or meta.get("repoUrl")
case _:
return None
def normalize(value: Any) -> str:
if value is None:
return ""
return str(value).strip()
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--catalog", default="app-catalog/catalog.json")
parser.add_argument("--apps-dir", default="apps")
parser.add_argument(
"--strict",
action="store_true",
help="exit non-zero when missing entries or metadata drift are found",
)
parser.add_argument(
"--release",
action="store_true",
help="suppress known internal/legacy-stack entries so output is release-actionable",
)
args = parser.parse_args()
catalog = load_catalog(Path(args.catalog))
manifests = load_manifests(Path(args.apps_dir))
catalog_ids = set(catalog)
manifest_ids = set(manifests)
missing_manifests = sorted(catalog_ids - manifest_ids)
missing_catalog = sorted(manifest_ids - catalog_ids)
if args.release:
missing_manifests = [app_id for app_id in missing_manifests if app_id not in LEGACY_STACK_CATALOG_IDS]
missing_catalog = [app_id for app_id in missing_catalog if app_id not in INTERNAL_MANIFEST_IDS]
compared_fields = [
"title",
"version",
"description",
"dockerImage",
"category",
"tier",
"icon",
"repoUrl",
]
drift: list[str] = []
for app_id in sorted(catalog_ids & manifest_ids):
catalog_app = catalog[app_id]
manifest_app = manifests[app_id]["app"]
for field in compared_fields:
catalog_val = normalize(catalog_app.get(field))
manifest_val = normalize(manifest_value(manifest_app, field))
if catalog_val and manifest_val and catalog_val != manifest_val:
drift.append(f"{app_id}: {field}: catalog={catalog_val!r} manifest={manifest_val!r}")
print(
json.dumps(
{
"catalog_apps": len(catalog),
"manifest_apps": len(manifests),
"missing_manifests": len(missing_manifests),
"missing_catalog": len(missing_catalog),
"metadata_drift": len(drift),
},
sort_keys=True,
)
)
for app_id in missing_manifests:
print(f"MISSING_MANIFEST {app_id}")
for app_id in missing_catalog:
print(f"MISSING_CATALOG {app_id}")
for item in drift:
print(f"DRIFT {item}")
if args.strict and (missing_manifests or missing_catalog or drift):
return 1
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,205 @@
#!/usr/bin/env python3
"""Sync public app catalog metadata from apps/*/manifest.yml.
Manifests are the source of truth for fields the runtime already needs
(`name`, `version`, `description`, container image, category, tier, icon,
repo URL). The catalog still owns presentation-only fields that manifests do
not carry yet, such as `author`, `requires`, `featured`, and rich
`containerConfig` notes.
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Any
import yaml
SYNC_FIELDS = ("title", "version", "description", "dockerImage", "category", "tier", "icon", "repoUrl")
def load_manifests(apps_dir: Path) -> dict[str, dict[str, Any]]:
manifests: dict[str, dict[str, Any]] = {}
for path in sorted(apps_dir.glob("*/manifest.yml")):
with path.open("r", encoding="utf-8") as fh:
data = yaml.safe_load(fh)
if not isinstance(data, dict) or not isinstance(data.get("app"), dict):
continue
app = data["app"]
app_id = app.get("id")
if app_id:
manifests[str(app_id)] = app
return manifests
def metadata(app: dict[str, Any]) -> dict[str, Any]:
value = app.get("metadata")
return value if isinstance(value, dict) else {}
def manifest_catalog_values(app: dict[str, Any]) -> dict[str, str]:
meta = metadata(app)
container = app.get("container") if isinstance(app.get("container"), dict) else {}
values = {
"title": app.get("name"),
"version": app.get("version"),
"description": app.get("description"),
"dockerImage": container.get("image"),
"category": app.get("category") or meta.get("category"),
"tier": meta.get("tier"),
"icon": meta.get("icon"),
"repoUrl": meta.get("repo") or meta.get("repoUrl") or meta.get("source"),
}
return {key: str(value) for key, value in values.items() if value is not None and str(value).strip()}
def manifest_launch_port(app: dict[str, Any]) -> int | None:
"""Return the manifest-owned public UI port, when it is unambiguous."""
interfaces = app.get("interfaces")
if isinstance(interfaces, dict):
main = interfaces.get("main")
if isinstance(main, dict) and main.get("type") == "ui":
port = main.get("port")
if isinstance(port, int):
return port
if isinstance(port, str) and port.isdigit():
return int(port)
ports = app.get("ports")
if not isinstance(ports, list):
return None
tcp_ports = [
item.get("host")
for item in ports
if isinstance(item, dict) and str(item.get("protocol", "tcp")).lower() == "tcp"
]
if len(tcp_ports) != 1:
return None
port = tcp_ports[0]
if isinstance(port, int):
return port
if isinstance(port, str) and port.isdigit():
return int(port)
return None
def manifest_opens_in_new_tab(app: dict[str, Any]) -> bool:
"""Return whether manifest launch metadata opts the app out of iframe launch."""
launch = metadata(app).get("launch")
if not isinstance(launch, dict):
return False
return launch.get("open_in_new_tab") is True
def ts_string(value: str) -> str:
return json.dumps(value, ensure_ascii=True)
def render_app_session_config(manifests: dict[str, dict[str, Any]]) -> str:
ports: dict[str, int] = {}
titles: dict[str, str] = {}
new_tab_apps: list[str] = []
for app_id, app in sorted(manifests.items()):
name = app.get("name")
if isinstance(name, str) and name.strip():
titles[app_id] = name.strip()
port = manifest_launch_port(app)
if port:
ports[app_id] = port
if manifest_opens_in_new_tab(app):
new_tab_apps.append(app_id)
lines = [
"/** Generated by scripts/generate-app-catalog.py. Do not edit manually. */",
"",
"export const GENERATED_APP_PORTS: Record<string, number> = {",
]
for app_id, port in ports.items():
lines.append(f" {ts_string(app_id)}: {port},")
lines.extend([
"}",
"",
"export const GENERATED_APP_TITLES: Record<string, string> = {",
])
for app_id, title in titles.items():
lines.append(f" {ts_string(app_id)}: {ts_string(title)},")
lines.extend([
"}",
"",
"export const GENERATED_NEW_TAB_APPS = new Set<string>([",
])
for app_id in new_tab_apps:
lines.append(f" {ts_string(app_id)},")
lines.extend(["])", ""])
return "\n".join(lines)
def sync_catalog(path: Path, manifests: dict[str, dict[str, Any]]) -> int:
with path.open("r", encoding="utf-8") as fh:
catalog = json.load(fh)
apps = catalog.get("apps")
if not isinstance(apps, list):
raise ValueError(f"{path}: expected .apps to be a list")
changed = 0
for catalog_app in apps:
if not isinstance(catalog_app, dict):
continue
app_id = catalog_app.get("id")
if not app_id or str(app_id) not in manifests:
continue
values = manifest_catalog_values(manifests[str(app_id)])
for field in SYNC_FIELDS:
if field not in values:
continue
old = catalog_app.get(field)
new = values[field]
if old != new:
catalog_app[field] = new
changed += 1
path.write_text(json.dumps(catalog, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
return changed
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--apps-dir", default="apps")
parser.add_argument(
"--catalog",
action="append",
default=[],
help="Catalog JSON path to update. May be passed multiple times.",
)
parser.add_argument(
"--app-session-config",
default="neode-ui/src/views/appSession/generatedAppSessionConfig.ts",
help="Generated TypeScript app-session metadata path. Pass an empty string to skip.",
)
args = parser.parse_args()
catalogs = args.catalog or ["app-catalog/catalog.json", "neode-ui/public/catalog.json"]
manifests = load_manifests(Path(args.apps_dir))
total = 0
for catalog in catalogs:
changed = sync_catalog(Path(catalog), manifests)
total += changed
print(f"{catalog}: updated {changed} fields")
if args.app_session_config:
path = Path(args.app_session_config)
content = render_app_session_config(manifests)
old = path.read_text(encoding="utf-8") if path.exists() else ""
if old != content:
path.write_text(content, encoding="utf-8")
print(f"{path}: updated")
else:
print(f"{path}: updated 0 fields")
print(f"total_updated={total}")
return 0
if __name__ == "__main__":
raise SystemExit(main())