archy/releases/app-catalog.json
archipelago fb72e3e159 fix(fedimint): gateway/guardian follow the running bitcoin container via {{BITCOIN_HOST}}
The gateway crash-looped dialing bitcoind at host.archipelago:8332 (the
host gateway IP, where bitcoind does not listen). Root-cause fix, three
parts:

- fedimint-gateway manifest: --bitcoind-url now comes from
  $FM_BITCOIND_URL, filled by a {{BITCOIN_HOST}} derived-env — works on
  Knots, Core, or any future Bitcoin distro.
- fedimint manifest: same derived-env replaces the hardcoded
  FM_BITCOIND_URL=http://bitcoin-knots:8332 environment entry.
- orchestrator: removed the hardcoded FM_BITCOIND_URL=bitcoin-knots
  override that clobbered the derived-env, and bitcoin_host() now
  accepts any running bitcoin*/bitcoin container (excluding -ui and
  archy-* sidecars), preferring the known names in order.

Catalog regenerated + signed (nodes install from the signed catalog, so
the manifest fixes only reach them via this regen).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 22:07:15 -04:00

4747 lines
162 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"apps": {
"adguardhome": {
"image": "146.59.87.168:3000/lfg2025/adguardhome:v0.107.55",
"version": "v0.107.55"
},
"aiui": {
"manifest": {
"app": {
"container": {
"image": "localhost/archipelago-aiui:latest",
"pull_policy": "always"
},
"description": "Conversational AI interface for Archipelago. Quarantined — communicates only via context broker.",
"health_check": {
"endpoint": "http://localhost:80",
"interval": "60s",
"path": "/",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "aiui",
"internal": true,
"name": "AI Assistant",
"ports": [
{
"bind": "127.0.0.1",
"container": 80,
"host": 5180,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 1,
"disk_limit": "1Gi",
"memory_limit": "512Mi"
},
"security": {
"apparmor_profile": "aiui",
"capabilities": [],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": true,
"seccomp_profile": "default",
"user": 1000
},
"version": "0.1.0"
}
},
"version": "0.1.0"
},
"archy-btcpay-db": {
"manifest": {
"app": {
"bitcoin_integration": {
"rpc_access": "none",
"sync_required": false
},
"container": {
"data_uid": "100998:100998",
"image": "146.59.87.168:3000/lfg2025/postgres:15.17",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "POSTGRES_PASSWORD",
"secret_file": "btcpay-db-password"
}
]
},
"dependencies": [
{
"storage": "20Gi"
}
],
"description": "Postgres backend for BTCPay and NBXplorer.",
"environment": [
"POSTGRES_DB=btcpay",
"POSTGRES_USER=btcpay"
],
"health_check": {
"endpoint": "localhost:5432",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "archy-btcpay-db",
"name": "BTCPay Postgres",
"ports": [],
"resources": {
"disk_limit": "20Gi",
"memory_limit": "1Gi"
},
"security": {
"capabilities": [
"CHOWN",
"FOWNER",
"SETUID",
"SETGID",
"DAC_OVERRIDE"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "15.17",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/postgres-btcpay",
"target": "/var/lib/postgresql/data",
"type": "bind"
}
]
}
},
"version": "15.17"
},
"archy-mempool-db": {
"manifest": {
"app": {
"bitcoin_integration": {
"rpc_access": "none",
"sync_required": false
},
"container": {
"data_uid": "100998:100998",
"image": "146.59.87.168:3000/lfg2025/mariadb:11.4.10",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "MYSQL_PASSWORD",
"secret_file": "mempool-db-password"
},
{
"key": "MYSQL_ROOT_PASSWORD",
"secret_file": "mysql-root-db-password"
}
]
},
"dependencies": [
{
"storage": "20Gi"
}
],
"description": "MariaDB backend for the mempool explorer stack.",
"environment": [
"MYSQL_DATABASE=mempool",
"MYSQL_USER=mempool"
],
"health_check": {
"endpoint": "localhost:3306",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "archy-mempool-db",
"name": "Mempool MariaDB",
"ports": [],
"resources": {
"disk_limit": "20Gi",
"memory_limit": "512Mi"
},
"security": {
"capabilities": [
"CHOWN",
"FOWNER",
"SETUID",
"SETGID",
"DAC_OVERRIDE"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "11.4.10",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/mysql-mempool",
"target": "/var/lib/mysql",
"type": "bind"
}
]
}
},
"version": "11.4.10"
},
"archy-mempool-web": {
"manifest": {
"app": {
"bitcoin_integration": {
"rpc_access": "none",
"sync_required": false
},
"container": {
"image": "146.59.87.168:3000/lfg2025/mempool-frontend:v3.0.1",
"network": "archy-net",
"pull_policy": "if-not-present"
},
"container_name": "mempool",
"dependencies": [
{
"app_id": "mempool-api",
"version": ">=3.0.0"
}
],
"description": "Frontend web UI for mempool explorer.",
"environment": [
"FRONTEND_HTTP_PORT=8080",
"BACKEND_MAINNET_HTTP_HOST=mempool-api"
],
"health_check": {
"endpoint": "http://127.0.0.1:8080",
"interval": "30s",
"path": "/",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "archy-mempool-web",
"name": "Mempool Web",
"ports": [
{
"container": 8080,
"host": 4080,
"protocol": "tcp"
}
],
"resources": {
"memory_limit": "512Mi"
},
"security": {
"capabilities": [],
"network_policy": "isolated",
"readonly_root": false
},
"version": "3.0.1"
}
},
"version": "3.0.1"
},
"archy-nbxplorer": {
"manifest": {
"app": {
"bitcoin_integration": {
"rpc_access": "read-only",
"sync_required": true
},
"container": {
"image": "146.59.87.168:3000/lfg2025/nbxplorer:2.6.0",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "NBXPLORER_BTCRPCPASSWORD",
"secret_file": "bitcoin-rpc-password"
},
{
"key": "BTCPAY_DB_PASS",
"secret_file": "btcpay-db-password"
}
]
},
"dependencies": [
{
"app_id": "bitcoin-core",
"version": ">=26.0"
},
{
"app_id": "archy-btcpay-db",
"version": ">=15.17"
}
],
"description": "BTCPay blockchain indexer service.",
"environment": [
"NBXPLORER_DATADIR=/data",
"NBXPLORER_NETWORK=mainnet",
"NBXPLORER_CHAINS=btc",
"NBXPLORER_BIND=0.0.0.0:32838",
"NBXPLORER_BTCRPCURL=http://bitcoin-knots:8332",
"NBXPLORER_BTCRPCUSER=archipelago",
"NBXPLORER_BTCNODEENDPOINT=bitcoin-knots:8333",
"NBXPLORER_NOAUTH=1",
"NBXPLORER_POSTGRES=Username=btcpay;Password=${BTCPAY_DB_PASS};Host=archy-btcpay-db;Port=5432;Database=nbxplorer"
],
"health_check": {
"endpoint": "http://localhost:32838",
"interval": "30s",
"path": "/",
"retries": 5,
"timeout": "30s",
"type": "http"
},
"id": "archy-nbxplorer",
"name": "NBXplorer",
"ports": [
{
"container": 32838,
"host": 32838,
"protocol": "tcp"
}
],
"resources": {
"disk_limit": "20Gi",
"memory_limit": "2Gi"
},
"security": {
"capabilities": [],
"network_policy": "isolated",
"readonly_root": false
},
"version": "2.6.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/nbxplorer",
"target": "/data",
"type": "bind"
}
]
}
},
"version": "2.6.0"
},
"barkd": {
"manifest": {
"app": {
"container": {
"data_uid": "1000:1000",
"generated_secrets": [
{
"kind": "hex32",
"name": "barkd-secret"
}
],
"image": "146.59.87.168:3000/lfg2025/barkd:0.3.0",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "BARKD_SECRET",
"secret_file": "barkd-secret"
}
]
},
"dependencies": [
{
"storage": "1Gi"
}
],
"description": "Ark protocol wallet daemon (barkd). Lets the node hold self-custodial off-chain bitcoin via an Ark server; the wallet talks to it over a local REST API. Signet by default while Ark matures.",
"environment": [
"BARKD_DATADIR=/data",
"BARKD_BIND_HOST=0.0.0.0",
"BARKD_BIND_PORT=3535"
],
"health_check": {
"endpoint": "localhost:3535",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "barkd",
"name": "Ark Wallet",
"ports": [
{
"container": 3535,
"host": 3535,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 1,
"disk_limit": "1Gi",
"memory_limit": "512Mi"
},
"security": {
"network_policy": "bridge",
"readonly_root": true
},
"version": "0.3.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/barkd",
"target": "/data",
"type": "bind"
}
]
}
},
"version": "0.3.0"
},
"bitcoin-core": {
"manifest": {
"app": {
"bitcoin_integration": {
"pruning_support": true,
"rpc_access": "admin",
"sync_required": true,
"testnet_support": false
},
"container": {
"custom_args": [
"BITCOIND=\"$(command -v bitcoind || true)\"; if [ -z \"$BITCOIND\" ]; then\n BITCOIND=\"$(find /opt -path '*/bin/bitcoind' -type f 2>/dev/null | sort | tail -n 1)\";\nfi; if [ -z \"$BITCOIND\" ]; then\n echo \"bitcoind not found in image\" >&2;\n exit 127;\nfi; RPC_USER=\"$(printenv BITCOIN_RPC_USER)\"; RPC_PASS=\"$(printenv BITCOIN_RPC_PASS)\"; RPC_CONF=\"/tmp/rpc.conf\"; umask 077; { echo \"rpcuser=$RPC_USER\"; echo \"rpcpassword=$RPC_PASS\"; } > \"$RPC_CONF\"; RPC_TXRELAY_AUTH=\"$(printenv BITCOIN_RPC_TXRELAY_RPCAUTH || 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\n RPC_TXRELAY_FLAGS=\"$RPC_TXRELAY_FLAGS -rpcauth=$RPC_TXRELAY_AUTH -rpcwhitelist=txrelay:sendrawtransaction,submitpackage,testmempoolaccept,getmempoolinfo,getrawmempool,getmempoolentry,getnetworkinfo,getblockchaininfo,getblockcount,getblockhash,getblock,getblockheader,getrawtransaction,gettxout,gettxspendingprevout,decoderawtransaction,decodescript,estimatesmartfee,uptime,ping,getconnectioncount,getpeerinfo,getindexinfo,getdeploymentinfo,getchaintips\";\nfi; if [ \"${DISK_GB_VALUE:-0}\" -lt 1000 ]; then\n exec \"$BITCOIND\" -datadir=/home/bitcoin/.bitcoin -conf=\"$RPC_CONF\" -printtoconsole=0 -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=1024 -par=0 -maxconnections=125 $RPC_HEADROOM $RPC_TXRELAY_FLAGS;\nelse\n exec \"$BITCOIND\" -datadir=/home/bitcoin/.bitcoin -conf=\"$RPC_CONF\" -printtoconsole=0 -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;\nfi"
],
"data_uid": "100101:100101",
"derived_env": [
{
"key": "DISK_GB",
"template": "{{DISK_GB}}"
}
],
"entrypoint": [
"sh",
"-lc"
],
"image": "146.59.87.168:3000/lfg2025/bitcoin:28.4",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "BITCOIN_RPC_PASS",
"secret_file": "bitcoin-rpc-password"
},
{
"key": "BITCOIN_RPC_TXRELAY_RPCAUTH",
"secret_file": "bitcoin-rpc-txrelay-rpcauth"
}
]
},
"container_name": "bitcoin-core",
"dependencies": [
{
"storage": "500Gi"
}
],
"description": "Reference Bitcoin Core node with dynamic prune/full-mode startup based on host disk.",
"environment": [
"BITCOIN_RPC_USER=archipelago"
],
"health_check": {
"endpoint": "localhost:8332",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "bitcoin-core",
"name": "Bitcoin Core",
"ports": [
{
"bind": "127.0.0.1",
"container": 8332,
"host": 8332,
"protocol": "tcp"
},
{
"container": 8333,
"host": 8333,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 0,
"disk_limit": "500Gi",
"memory_limit": "4Gi"
},
"security": {
"capabilities": [
"CHOWN",
"FOWNER",
"SETUID",
"SETGID",
"DAC_OVERRIDE"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "28.4.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/bitcoin",
"target": "/home/bitcoin/.bitcoin",
"type": "bind"
}
]
}
},
"version": "latest",
"versions": [
{
"default": true,
"image": "146.59.87.168:3000/lfg2025/bitcoin:latest",
"version": "latest"
},
{
"image": "146.59.87.168:3000/lfg2025/bitcoin:31.0",
"version": "31.0"
},
{
"image": "146.59.87.168:3000/lfg2025/bitcoin:30.2",
"version": "30.2"
},
{
"image": "146.59.87.168:3000/lfg2025/bitcoin:29.3",
"version": "29.3"
},
{
"image": "146.59.87.168:3000/lfg2025/bitcoin:29.2",
"version": "29.2"
},
{
"image": "146.59.87.168:3000/lfg2025/bitcoin:28.4",
"version": "28.4.0"
},
{
"image": "146.59.87.168:3000/lfg2025/bitcoin:27.2",
"version": "27.2"
},
{
"deprecated": true,
"image": "146.59.87.168:3000/lfg2025/bitcoin:26.2",
"version": "26.2"
},
{
"deprecated": true,
"image": "146.59.87.168:3000/lfg2025/bitcoin:25.2",
"version": "25.2"
}
]
},
"bitcoin-knots": {
"image": "146.59.87.168:3000/lfg2025/bitcoin-knots:latest",
"manifest": {
"app": {
"bitcoin_integration": {
"pruning_support": true,
"rpc_access": "admin",
"sync_required": true,
"testnet_support": false
},
"container": {
"custom_args": [
"BITCOIND=\"$(command -v bitcoind || true)\"; if [ -z \"$BITCOIND\" ]; then\n BITCOIND=\"$(find /opt -path '*/bin/bitcoind' -type f 2>/dev/null | sort | tail -n 1)\";\nfi; if [ -z \"$BITCOIND\" ]; then\n echo \"bitcoind not found in image\" >&2;\n exit 127;\nfi; RPC_USER=\"$(printenv BITCOIN_RPC_USER)\"; RPC_PASS=\"$(printenv BITCOIN_RPC_PASS)\"; RPC_CONF=\"/tmp/rpc.conf\"; umask 077; { echo \"rpcuser=$RPC_USER\"; echo \"rpcpassword=$RPC_PASS\"; } > \"$RPC_CONF\"; RPC_TXRELAY_AUTH=\"$(printenv BITCOIN_RPC_TXRELAY_RPCAUTH || 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\n RPC_TXRELAY_FLAGS=\"$RPC_TXRELAY_FLAGS -rpcauth=$RPC_TXRELAY_AUTH -rpcwhitelist=txrelay:sendrawtransaction,submitpackage,testmempoolaccept,getmempoolinfo,getrawmempool,getmempoolentry,getnetworkinfo,getblockchaininfo,getblockcount,getblockhash,getblock,getblockheader,getrawtransaction,gettxout,gettxspendingprevout,decoderawtransaction,decodescript,estimatesmartfee,uptime,ping,getconnectioncount,getpeerinfo,getindexinfo,getdeploymentinfo,getchaintips\";\nfi; if [ \"${DISK_GB_VALUE:-0}\" -lt 1000 ]; then\n exec \"$BITCOIND\" -datadir=/home/bitcoin/.bitcoin -conf=\"$RPC_CONF\" -printtoconsole=0 -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;\nelse\n exec \"$BITCOIND\" -datadir=/home/bitcoin/.bitcoin -conf=\"$RPC_CONF\" -printtoconsole=0 -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;\nfi"
],
"data_uid": "100101:100101",
"derived_env": [
{
"key": "DISK_GB",
"template": "{{DISK_GB}}"
}
],
"entrypoint": [
"sh",
"-lc"
],
"image": "146.59.87.168:3000/lfg2025/bitcoin-knots:latest",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "BITCOIN_RPC_PASS",
"secret_file": "bitcoin-rpc-password"
},
{
"key": "BITCOIN_RPC_TXRELAY_RPCAUTH",
"secret_file": "bitcoin-rpc-txrelay-rpcauth"
}
]
},
"container_name": "bitcoin-knots",
"dependencies": [
{
"storage": "500Gi"
}
],
"description": "Full Bitcoin Knots node with dynamic prune/full-mode startup based on host disk.",
"environment": [
"BITCOIN_RPC_USER=archipelago"
],
"health_check": {
"endpoint": "localhost:8332",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "bitcoin-knots",
"name": "Bitcoin Knots",
"ports": [
{
"bind": "127.0.0.1",
"container": 8332,
"host": 8332,
"protocol": "tcp"
},
{
"container": 8333,
"host": 8333,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 0,
"disk_limit": "500Gi",
"memory_limit": "8Gi"
},
"security": {
"capabilities": [
"CHOWN",
"FOWNER",
"SETUID",
"SETGID",
"DAC_OVERRIDE"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "28.1.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/bitcoin",
"target": "/home/bitcoin/.bitcoin",
"type": "bind"
}
]
}
},
"version": "latest",
"versions": [
{
"default": true,
"image": "146.59.87.168:3000/lfg2025/bitcoin-knots:29.3.knots20260508",
"version": "latest"
},
{
"image": "146.59.87.168:3000/lfg2025/bitcoin-knots:29.3.knots20260508",
"version": "29.3.knots20260508"
},
{
"image": "146.59.87.168:3000/lfg2025/bitcoin-knots:29.3.knots20260507",
"version": "29.3.knots20260507"
},
{
"image": "146.59.87.168:3000/lfg2025/bitcoin-knots:29.3.knots20260210",
"version": "29.3.knots20260210"
},
{
"image": "146.59.87.168:3000/lfg2025/bitcoin-knots:29.2.knots20251110",
"version": "29.2.knots20251110"
}
]
},
"bitcoin-ui": {
"image": "146.59.87.168:3000/lfg2025/bitcoin-ui:1.7.84-alpha",
"manifest": {
"app": {
"container": {
"build": {
"context": "/opt/archipelago/docker/bitcoin-ui",
"dockerfile": "Dockerfile",
"tag": "localhost/bitcoin-ui:local"
}
},
"dependencies": [
{
"app_id": "bitcoin-core"
}
],
"description": "Archipelago-native HTTP proxy + static site for interacting with the\nBitcoin Core / Bitcoin Knots JSON-RPC. Runs nginx inside a container\nand reverse-proxies /bitcoin-rpc/ to 127.0.0.1:8332 on the host. The\nupstream Authorization header is substituted from\n/var/lib/archipelago/secrets/bitcoin-rpc-password by the prod\norchestrator's pre-start hook, rendered into an nginx.conf that is\nbind-mounted read-only at container start.\n",
"environment": [],
"health_check": {
"endpoint": "http://127.0.0.1:8334",
"interval": "30s",
"path": "/",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "bitcoin-ui",
"name": "Bitcoin UI",
"ports": [],
"resources": {
"memory_limit": "128Mi"
},
"security": {
"network_policy": "host",
"readonly_root": false
},
"version": "1.0.0",
"volumes": [
{
"options": [
"ro"
],
"source": "/var/lib/archipelago/bitcoin-ui/nginx.conf",
"target": "/etc/nginx/conf.d/default.conf",
"type": "bind"
}
]
}
},
"version": "1.7.84-alpha"
},
"botfights": {
"manifest": {
"app": {
"category": "community",
"container": {
"image": "146.59.87.168:3000/lfg2025/botfights:1.1.0",
"pull_policy": "always"
},
"dependencies": [
{
"storage": "500Mi"
}
],
"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.",
"environment": [
"NODE_ENV=production"
],
"health_check": {
"endpoint": "http://localhost:9100",
"interval": "30s",
"path": "/api/health",
"retries": 3,
"start_period": "30s",
"timeout": "10s",
"type": "http"
},
"id": "botfights",
"interfaces": {
"main": {
"description": "Bot arena and arcade fighter with controller support",
"name": "Web UI",
"path": "/",
"port": 9100,
"protocol": "http",
"type": "ui"
}
},
"metadata": {
"author": "Dorian",
"icon": "/assets/img/app-icons/botfights.svg",
"license": "MIT",
"repo": "https://botfights.net",
"tags": [
"bitcoin",
"gaming",
"arcade",
"fighter",
"bots",
"competition",
"controller"
]
},
"name": "BotFights",
"ports": [
{
"container": 9100,
"host": 9100,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 2,
"disk_limit": "500Mi",
"memory_limit": "512Mi"
},
"security": {
"apparmor_profile": "default",
"capabilities": [],
"network_policy": "bridge",
"no_new_privileges": true,
"readonly_root": true,
"seccomp_profile": "default",
"user": 1001
},
"version": "1.1.0",
"volumes": [
{
"source": "botfights-data",
"target": "/app/server/data",
"type": "bind"
},
{
"options": [
"rw",
"noexec",
"nosuid",
"size=64m"
],
"target": "/tmp",
"type": "tmpfs"
}
]
}
},
"version": "1.1.0"
},
"btcpay": {
"image": "docker.io/btcpayserver/btcpayserver:2.3.9",
"images": {
"archy-btcpay-db": "146.59.87.168:3000/lfg2025/postgres:15.17",
"archy-nbxplorer": "146.59.87.168:3000/lfg2025/nbxplorer:2.6.0",
"btcpay-server": "docker.io/btcpayserver/btcpayserver:2.3.9"
},
"version": "2.3.9"
},
"btcpay-server": {
"manifest": {
"app": {
"bitcoin_integration": {
"rpc_access": "read-only",
"sync_required": true
},
"container": {
"derived_env": [
{
"key": "BTCPAY_HOST",
"template": "{{HOST_IP}}:23000"
}
],
"image": "docker.io/btcpayserver/btcpayserver:2.3.9",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "BTCPAY_BTCRPCPASSWORD",
"secret_file": "bitcoin-rpc-password"
},
{
"key": "BTCPAY_DB_PASS",
"secret_file": "btcpay-db-password"
},
{
"key": "BTCPAY_BTCLIGHTNING",
"optional": true,
"secret_file": "btcpay-lnd-connection"
}
]
},
"dependencies": [
{
"app_id": "bitcoin-core",
"version": ">=26.0"
},
{
"app_id": "archy-btcpay-db",
"version": ">=15.17"
},
{
"app_id": "archy-nbxplorer",
"version": ">=2.6.0"
}
],
"description": "Self-hosted Bitcoin payment processor. Accept Bitcoin payments without intermediaries.",
"environment": [
"ASPNETCORE_URLS=http://0.0.0.0:49392",
"BTCPAY_PROTOCOL=http",
"BTCPAY_CHAINS=btc",
"BTCPAY_PLUGINDIR=/datadir/Plugins",
"BTCPAY_BTCEXPLORERURL=http://archy-nbxplorer:32838",
"BTCPAY_BTCRPCURL=http://bitcoin-knots:8332",
"BTCPAY_BTCRPCUSER=archipelago",
"BTCPAY_POSTGRES=Username=btcpay;Password=${BTCPAY_DB_PASS};Host=archy-btcpay-db;Port=5432;Database=btcpay"
],
"health_check": {
"endpoint": "http://localhost:49392",
"interval": "30s",
"path": "/",
"retries": 5,
"timeout": "30s",
"type": "http"
},
"id": "btcpay-server",
"interfaces": {
"main": {
"description": "BTCPay Server dashboard",
"name": "Web UI",
"path": "/",
"port": 23000,
"protocol": "http",
"type": "ui"
}
},
"lightning_integration": {
"invoice_management": true,
"payment_processing": false
},
"metadata": {
"launch": {
"open_in_new_tab": true
}
},
"name": "BTCPay Server",
"ports": [
{
"container": 49392,
"host": 23000,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 2,
"disk_limit": "20Gi",
"memory_limit": "2Gi"
},
"security": {
"capabilities": [],
"network_policy": "isolated",
"readonly_root": false
},
"version": "2.3.9",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/btcpay",
"target": "/datadir",
"type": "bind"
}
]
}
},
"version": "2.3.9"
},
"core-lightning": {
"manifest": {
"app": {
"bitcoin_integration": {
"rpc_access": "admin",
"sync_required": true
},
"container": {
"image": "elementsproject/lightningd:v23.08.2",
"image_signature": "cosign://...",
"pull_policy": "verify-signature"
},
"dependencies": [
{
"app_id": "bitcoin-core",
"version": ">=26.0"
}
],
"description": "Lightning Network implementation in C. Lightweight alternative to LND.",
"environment": [
"BITCOIND_RPCURL=http://bitcoin-core:8332",
"BITCOIND_RPCUSER=${BITCOIN_RPC_USER}",
"BITCOIND_RPCPASS=${BITCOIN_RPC_PASSWORD}",
"NETWORK=bitcoin"
],
"health_check": {
"endpoint": "lightning-cli getinfo",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "exec"
},
"id": "core-lightning",
"lightning_integration": {
"channel_management": true,
"payment_routing": true
},
"name": "Core Lightning (CLN)",
"ports": [
{
"container": 9735,
"host": 9736,
"protocol": "tcp"
},
{
"container": 9835,
"host": 9835,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 1,
"disk_limit": "5Gi",
"memory_limit": "512Mi"
},
"security": {
"apparmor_profile": "core-lightning",
"capabilities": [
"NET_BIND_SERVICE"
],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": true,
"seccomp_profile": "default",
"user": 1000
},
"version": "23.08.2",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/core-lightning",
"target": "/home/clightning/.lightning",
"type": "bind"
}
]
}
},
"version": "23.08.2"
},
"cryptpad": {
"image": "146.59.87.168:3000/lfg2025/cryptpad:2024.12.0",
"version": "2024.12.0"
},
"did-wallet": {
"manifest": {
"app": {
"container": {
"image": "archipelago/did-wallet:1.0.0",
"image_signature": "cosign://...",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "2Gi"
}
],
"description": "Web5 wallet with Decentralized Identifier (DID) support. Manage your digital identity and Web5 assets.",
"environment": [
"WALLET_STORAGE=/app/wallet"
],
"health_check": {
"endpoint": "http://127.0.0.1:8080",
"interval": "30s",
"path": "/health",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "did-wallet",
"name": "Web5 DID Wallet",
"ports": [
{
"container": 8080,
"host": 8088,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 1,
"disk_limit": "2Gi",
"memory_limit": "512Mi"
},
"security": {
"apparmor_profile": "did-wallet",
"capabilities": [],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": true,
"seccomp_profile": "default",
"user": 1000
},
"version": "1.0.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/did-wallet",
"target": "/app/wallet",
"type": "bind"
}
],
"web5_integration": {
"bitcoin_integration": true,
"did_support": true,
"wallet_functionality": true
}
}
},
"version": "1.0.0"
},
"electrs-ui": {
"image": "146.59.87.168:3000/lfg2025/electrs-ui:latest",
"manifest": {
"app": {
"container": {
"build": {
"context": "/opt/archipelago/docker/electrs-ui",
"dockerfile": "Dockerfile",
"tag": "localhost/electrs-ui:local"
}
},
"dependencies": [],
"description": "Archipelago-native HTTP frontend for electrs/electrumx status. Runs\nnginx inside a container, serves static assets, and proxies\n/electrs-status to the archipelago backend on 127.0.0.1:5678.\n",
"environment": [],
"health_check": {
"endpoint": "http://127.0.0.1:50002",
"interval": "30s",
"path": "/",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "electrs-ui",
"name": "Electrs UI",
"ports": [],
"resources": {
"memory_limit": "64Mi"
},
"security": {
"network_policy": "host",
"readonly_root": false
},
"version": "1.0.0",
"volumes": []
}
},
"version": "latest"
},
"electrumx": {
"image": "146.59.87.168:3000/lfg2025/electrumx:v1.18.0",
"manifest": {
"app": {
"bitcoin_integration": {
"pruning_support": false,
"rpc_access": "read-only",
"sync_required": true
},
"container": {
"custom_args": [
"for h in bitcoin-knots bitcoin-core; do if getent hosts \"$h\" >/dev/null 2>&1; then BTC_HOST=\"$h\"; break; fi; done; export DAEMON_URL=\"http://archipelago:$(printenv BITCOIN_RPC_PASS)@${BTC_HOST:-bitcoin-knots}:8332/\"; exec electrumx_server"
],
"data_uid": "1000:1000",
"entrypoint": [
"sh",
"-lc"
],
"image": "146.59.87.168:3000/lfg2025/electrumx:v1.18.0",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "BITCOIN_RPC_PASS",
"secret_file": "bitcoin-rpc-password"
}
]
},
"dependencies": [
{
"app_id": "bitcoin-knots",
"version": ">=26.0"
},
{
"storage": "50Gi"
},
"bitcoin:archival"
],
"description": "Electrum server indexing Bitcoin chain data for lightweight wallet queries.",
"environment": [
"COIN=Bitcoin",
"DB_DIRECTORY=/data",
"SERVICES=tcp://:50001,rpc://0.0.0.0:8000",
"CACHE_MB=1024",
"MAX_SEND=10000000"
],
"health_check": {
"endpoint": "localhost:50001",
"interval": "30s",
"retries": 3,
"start_period": "10m",
"timeout": "5s",
"type": "tcp"
},
"id": "electrumx",
"interfaces": {
"main": {
"description": "ElectrumX server status and connection details",
"name": "Web UI",
"port": 50002,
"protocol": "http",
"type": "ui"
}
},
"name": "ElectrumX",
"ports": [
{
"container": 50001,
"host": 50001,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 0,
"disk_limit": "50Gi",
"memory_limit": "6Gi"
},
"security": {
"capabilities": [
"DAC_OVERRIDE"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "1.18.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/electrumx",
"target": "/data",
"type": "bind"
}
]
}
},
"version": "v1.18.0"
},
"fedimint": {
"image": "146.59.87.168:3000/lfg2025/fedimintd:v0.10.0",
"manifest": {
"app": {
"bitcoin_integration": {
"rpc_access": "admin",
"sync_required": true
},
"container": {
"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\n echo \"Waiting for Bitcoin RPC sync at $FM_BITCOIND_URL...\";\n sleep 30;\ndone;\nexec fedimintd"
],
"data_uid": "1000:1000",
"derived_env": [
{
"key": "FM_P2P_URL",
"template": "fedimint://{{HOST_MDNS}}:8173"
},
{
"key": "FM_API_URL",
"template": "ws://{{HOST_MDNS}}:8174"
},
{
"key": "FM_BITCOIND_URL",
"template": "http://{{BITCOIN_HOST}}:8332"
}
],
"entrypoint": [
"sh",
"-lc"
],
"image": "146.59.87.168:3000/lfg2025/fedimintd:v0.10.0",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "FM_BITCOIND_PASSWORD",
"secret_file": "bitcoin-rpc-password"
}
]
},
"dependencies": [
{
"app_id": "bitcoin-core",
"version": ">=26.0"
},
{
"storage": "20Gi"
}
],
"description": "Federated Bitcoin minting service with built-in Guardian UI. Privacy-preserving Bitcoin custody.",
"environment": [
"FM_DATA_DIR=/data",
"FM_BITCOIND_USERNAME=archipelago",
"FM_BITCOIN_NETWORK=bitcoin",
"FM_BIND_P2P=0.0.0.0:8173",
"FM_BIND_API=0.0.0.0:8174",
"FM_BIND_UI=0.0.0.0:8175"
],
"health_check": {
"endpoint": "http://localhost:8175",
"interval": "30s",
"path": "/",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "fedimint",
"interfaces": {
"main": {
"description": "Fedimint Guardian wait/proxy UI",
"name": "Guardian UI",
"path": "/",
"port": 8175,
"protocol": "http",
"type": "ui"
}
},
"name": "Fedimint Guardian",
"ports": [
{
"container": 8173,
"host": 8173,
"protocol": "tcp"
},
{
"container": 8174,
"host": 8174,
"protocol": "tcp"
},
{
"container": 8175,
"host": 8177,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 4,
"disk_limit": "20Gi",
"memory_limit": "4Gi"
},
"security": {
"capabilities": [],
"network_policy": "isolated",
"readonly_root": true
},
"version": "0.10.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/fedimint",
"target": "/data",
"type": "bind"
}
]
}
},
"version": "v0.10.0"
},
"fedimint-clientd": {
"manifest": {
"app": {
"container": {
"data_uid": "1000:1000",
"generated_secrets": [
{
"kind": "hex16",
"name": "fmcd-password"
}
],
"image": "146.59.87.168:3000/lfg2025/fmcd:0.8.1",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "FMCD_PASSWORD",
"secret_file": "fmcd-password"
}
]
},
"dependencies": [
{
"storage": "2Gi"
}
],
"description": "Fedimint ecash client daemon (fmcd). Lets the node hold Fedimint ecash and join federations; the wallet talks to it over a local REST API.",
"environment": [
"FMCD_ADDR=0.0.0.0:8080",
"FMCD_MODE=rest",
"FMCD_DATA_DIR=/data",
"FMCD_INVITE_CODE=fed11qgqyj3mfwfhksw309uuxywtxxfjrjc35xuexverpxdsnxcnrxucxvenzveskgc3kvvun2c34xp3k2ep38yunzdpexcekxe3hvd3rvvmx8pnrvdenx5mnzvtzqqqjqt0t6pc3s5z0ynqjw9s4njf6svwgu59kweawc0vvrddcjeemw6yyn4pcdp"
],
"health_check": {
"endpoint": "localhost:8080",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "fedimint-clientd",
"name": "Fedimint Client",
"ports": [
{
"container": 8080,
"host": 8178,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 1,
"disk_limit": "2Gi",
"memory_limit": "1Gi"
},
"security": {
"capabilities": [
"CHOWN",
"DAC_OVERRIDE",
"FOWNER",
"SETUID",
"SETGID"
],
"network_policy": "bridge",
"readonly_root": true
},
"version": "0.8.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/fmcd",
"target": "/data",
"type": "bind"
}
]
}
},
"version": "0.8.0"
},
"fedimint-gateway": {
"image": "146.59.87.168:3000/lfg2025/gatewayd:v0.10.0",
"manifest": {
"app": {
"bitcoin_integration": {
"rpc_access": "admin",
"sync_required": true
},
"container": {
"custom_args": [
"if [ -f /lnd/tls.cert ] && [ -f /lnd/data/chain/bitcoin/mainnet/admin.macaroon ]; then\n exec gatewayd --data-dir /data --listen 0.0.0.0:8176 --bcrypt-password-hash \"$FEDI_HASH\" --network bitcoin --bitcoind-url \"$FM_BITCOIND_URL\" --bitcoind-username \"$FM_BITCOIND_USERNAME\" --bitcoind-password \"$FM_BITCOIND_PASSWORD\" lnd --lnd-rpc-host lnd:10009 --lnd-tls-cert /lnd/tls.cert --lnd-macaroon /lnd/data/chain/bitcoin/mainnet/admin.macaroon;\nelse\n exec gatewayd --data-dir /data --listen 0.0.0.0:8176 --bcrypt-password-hash \"$FEDI_HASH\" --network bitcoin --bitcoind-url \"$FM_BITCOIND_URL\" --bitcoind-username \"$FM_BITCOIND_USERNAME\" --bitcoind-password \"$FM_BITCOIND_PASSWORD\" ldk --ldk-lightning-port 9737 --ldk-alias archipelago-gateway;\nfi"
],
"data_uid": "1000:1000",
"derived_env": [
{
"key": "FM_BITCOIND_URL",
"template": "http://{{BITCOIN_HOST}}:8332"
}
],
"entrypoint": [
"sh",
"-lc"
],
"generated_secrets": [
{
"kind": "bcrypt",
"name": "fedimint-gateway-hash"
}
],
"image": "146.59.87.168:3000/lfg2025/gatewayd:v0.10.0",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "FM_BITCOIND_PASSWORD",
"secret_file": "bitcoin-rpc-password"
},
{
"key": "FEDI_HASH",
"secret_file": "fedimint-gateway-hash"
}
]
},
"dependencies": [
{
"app_id": "bitcoin-core",
"version": ">=26.0"
},
{
"app_id": "fedimint",
"version": ">=0.10.0"
}
],
"description": "Fedimint gateway service with automatic LND-or-LDK backend selection.",
"environment": [
"FM_BITCOIND_USERNAME=archipelago"
],
"health_check": {
"endpoint": "http://localhost:8176",
"interval": "30s",
"path": "/",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "fedimint-gateway",
"name": "Fedimint Gateway",
"ports": [
{
"container": 8176,
"host": 8176,
"protocol": "tcp"
},
{
"container": 9737,
"host": 9737,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 2,
"disk_limit": "10Gi",
"memory_limit": "2Gi"
},
"security": {
"capabilities": [],
"network_policy": "isolated",
"readonly_root": true
},
"version": "0.10.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/fedimint-gateway",
"target": "/data",
"type": "bind"
},
{
"options": [
"ro"
],
"source": "/var/lib/archipelago/lnd",
"target": "/lnd",
"type": "bind"
}
]
}
},
"version": "v0.10.0"
},
"filebrowser": {
"image": "146.59.87.168:3000/lfg2025/filebrowser:v2.27.0",
"manifest": {
"app": {
"bitcoin_integration": {
"rpc_access": "none",
"sync_required": false
},
"container": {
"custom_args": [
"--config",
"/data/.filebrowser.json"
],
"data_uid": "100000:100000",
"image": "146.59.87.168:3000/lfg2025/filebrowser:v2.27.0",
"network": "archy-net",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "10Gi"
}
],
"description": "Baseline Archipelago file manager service.",
"environment": [],
"health_check": {
"endpoint": "http://localhost:80",
"interval": "30s",
"path": "/health",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "filebrowser",
"name": "File Browser",
"ports": [
{
"container": 80,
"host": 8083,
"protocol": "tcp"
}
],
"resources": {
"disk_limit": "10Gi",
"memory_limit": "256Mi"
},
"security": {
"capabilities": [
"CHOWN",
"FOWNER",
"SETUID",
"SETGID",
"DAC_OVERRIDE",
"NET_BIND_SERVICE"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "2.27.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/filebrowser",
"target": "/srv",
"type": "bind"
},
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/filebrowser-data",
"target": "/data",
"type": "bind"
}
]
}
},
"version": "v2.27.0"
},
"fips": {
"image": "146.59.87.168:3000/lfg2025/fips:v0.1.0",
"version": "v0.1.0"
},
"fips-ui": {
"manifest": {
"app": {
"container": {
"build": {
"context": "/opt/archipelago/docker/fips-ui",
"dockerfile": "Dockerfile",
"tag": "localhost/fips-ui:local"
}
},
"description": "Archipelago-native dashboard for the FIPS mesh transport. Runs nginx\ninside a container with host networking, serves a static dashboard on\n:8336, and reverse-proxies /rpc/v1 to the archipelago backend on\n127.0.0.1:5678. All FIPS controls (status, seed anchors, reconnect,\nrestart, and stable-channel daemon updates) go through the existing\nfips.* RPC methods, authenticated by the browser's own archipelago\nsession — there is no separate secret to manage.\n",
"environment": [],
"health_check": {
"endpoint": "http://127.0.0.1:8336",
"interval": "30s",
"path": "/",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "fips-ui",
"name": "FIPS Mesh",
"ports": [],
"resources": {
"memory_limit": "128Mi"
},
"security": {
"network_policy": "host",
"readonly_root": false
},
"version": "1.0.0",
"volumes": []
}
},
"version": "1.0.0"
},
"gitea": {
"manifest": {
"app": {
"category": "development",
"container": {
"image": "docker.io/gitea/gitea:1.23",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "500Mi"
}
],
"description": "Self-hosted Git service with built-in container registry, CI/CD, and package hosting.",
"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": {
"endpoint": "http://localhost:3000",
"interval": "120s",
"path": "/",
"retries": 5,
"timeout": "30s",
"type": "http"
},
"id": "gitea",
"interfaces": {
"main": {
"description": "Gitea web interface",
"name": "Web UI",
"path": "/",
"port": 3001,
"protocol": "http",
"type": "ui"
}
},
"metadata": {
"features": [
"Git repositories with web UI",
"Built-in container/package registry",
"Issue tracking and pull requests",
"CI/CD via Gitea Actions",
"Lightweight SQLite deployment"
],
"icon": "/assets/img/app-icons/gitea.svg",
"launch": {
"open_in_new_tab": true
},
"repo": "https://gitea.com",
"tier": "optional"
},
"name": "Gitea",
"nginx_proxy": {
"extra_headers": [
"proxy_hide_header X-Frame-Options",
"proxy_hide_header Content-Security-Policy"
],
"listen": 3000,
"proxy_pass": "http://127.0.0.1:3001"
},
"ports": [
{
"container": 3000,
"host": 3001,
"protocol": "tcp"
},
{
"container": 22,
"host": 2222,
"protocol": "tcp"
}
],
"resources": {
"disk_limit": "500Mi",
"memory_limit": "256Mi"
},
"security": {
"capabilities": [
"CHOWN",
"FOWNER",
"SETUID",
"SETGID",
"DAC_OVERRIDE",
"NET_BIND_SERVICE"
],
"network_policy": "bridge",
"no_new_privileges": false,
"readonly_root": false
},
"version": "1.23",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/gitea/data",
"target": "/data",
"type": "bind"
},
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/gitea/config",
"target": "/etc/gitea",
"type": "bind"
}
]
}
},
"version": "1.23"
},
"grafana": {
"image": "146.59.87.168:3000/lfg2025/grafana:10.2.0",
"manifest": {
"app": {
"container": {
"data_uid": "472:472",
"image": "grafana/grafana:10.2.0",
"image_signature": "cosign://...",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "5Gi"
}
],
"description": "Analytics and monitoring platform. Visualize metrics and create dashboards.",
"environment": [
"GF_SECURITY_ADMIN_USER=admin",
"GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}",
"GF_SERVER_ROOT_URL=http://localhost:3000",
"GF_INSTALL_PLUGINS="
],
"health_check": {
"endpoint": "http://localhost:3000",
"interval": "30s",
"path": "/api/health",
"retries": 5,
"timeout": "30s",
"type": "http"
},
"id": "grafana",
"metadata": {
"launch": {
"open_in_new_tab": true
}
},
"name": "Grafana",
"ports": [
{
"container": 3000,
"host": 3000,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 2,
"disk_limit": "5Gi",
"memory_limit": "1Gi"
},
"security": {
"apparmor_profile": "grafana",
"capabilities": [],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": true,
"seccomp_profile": "default",
"user": 1000
},
"version": "10.2.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/grafana",
"target": "/var/lib/grafana",
"type": "bind"
}
]
}
},
"version": "10.2.0"
},
"homeassistant": {
"image": "146.59.87.168:3000/lfg2025/home-assistant:2026.7.3",
"manifest": {
"app": {
"container": {
"image": "146.59.87.168:3000/lfg2025/home-assistant:2026.7.3",
"network": "pasta",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "10Gi"
}
],
"description": "Open source home automation platform. Control and monitor your smart home devices.",
"devices": [],
"environment": [
"TZ=UTC"
],
"health_check": {
"endpoint": "localhost:8123",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "homeassistant",
"interfaces": {
"main": {
"description": "Home Assistant dashboard",
"name": "Web UI",
"path": "/",
"port": 8123,
"protocol": "http",
"type": "ui"
}
},
"metadata": {
"author": "Home Assistant",
"category": "home",
"icon": "/assets/img/app-icons/homeassistant.png",
"launch": {
"open_in_new_tab": true
},
"repo": "https://github.com/home-assistant/core"
},
"name": "Home Assistant",
"ports": [
{
"container": 8123,
"host": 8123,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 2,
"disk_limit": "10Gi",
"memory_limit": "512Mi"
},
"security": {
"apparmor_profile": "home-assistant",
"capabilities": [
"CHOWN",
"FOWNER",
"SETUID",
"SETGID",
"DAC_OVERRIDE",
"NET_BIND_SERVICE",
"NET_RAW"
],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": false,
"seccomp_profile": "default",
"user": 1000
},
"version": "2026.7.3",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/home-assistant",
"target": "/config",
"type": "bind"
}
]
}
},
"version": "2026.7.3"
},
"immich": {
"image": "146.59.87.168:3000/lfg2025/immich-server:release",
"images": {
"immich_postgres": "146.59.87.168:3000/lfg2025/immich-postgres:14-vectorchord0.4.3-pgvectors0.2.0",
"immich_redis": "146.59.87.168:3000/lfg2025/redis:7.4.8",
"immich_server": "146.59.87.168:3000/lfg2025/immich-server:release"
},
"manifest": {
"app": {
"container": {
"image": "146.59.87.168:3000/lfg2025/immich-server:release",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "DB_PASSWORD",
"secret_file": "immich-db-password"
}
]
},
"container_name": "immich_server",
"dependencies": [
{
"app_id": "immich-postgres"
},
{
"app_id": "immich-redis"
},
{
"storage": "200Gi"
}
],
"description": "Self-hosted photo and video backup with mobile apps and search.",
"environment": [
"DB_HOSTNAME=immich_postgres",
"DB_USERNAME=postgres",
"DB_DATABASE_NAME=immich",
"REDIS_HOSTNAME=immich_redis",
"UPLOAD_LOCATION=/usr/src/app/upload"
],
"health_check": {
"endpoint": "http://localhost:2283",
"interval": "30s",
"path": "/api/server/ping",
"retries": 20,
"timeout": "5s",
"type": "http"
},
"id": "immich",
"interfaces": {
"main": {
"description": "Immich photo library",
"name": "Web UI",
"path": "/",
"port": 2283,
"protocol": "http",
"type": "ui"
}
},
"metadata": {
"launch": {
"open_in_new_tab": true
}
},
"name": "Immich",
"ports": [
{
"container": 2283,
"host": 2283,
"protocol": "tcp"
}
],
"resources": {
"disk_limit": "200Gi",
"memory_limit": "2Gi"
},
"security": {
"capabilities": [
"CHOWN",
"DAC_OVERRIDE",
"FOWNER"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "2.7.4",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/immich",
"target": "/usr/src/app/upload",
"type": "bind"
}
]
}
},
"version": "release"
},
"immich-postgres": {
"manifest": {
"app": {
"container": {
"data_uid": "100998:100998",
"generated_secrets": [
{
"kind": "hex32",
"name": "immich-db-password"
}
],
"image": "146.59.87.168:3000/lfg2025/immich-postgres:14-vectorchord0.4.3-pgvectors0.2.0",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "POSTGRES_PASSWORD",
"secret_file": "immich-db-password"
}
]
},
"container_name": "immich_postgres",
"dependencies": [
{
"storage": "40Gi"
}
],
"description": "Postgres (pgvecto.rs / vectorchord) backend for Immich.",
"environment": [
"POSTGRES_USER=postgres",
"POSTGRES_DB=immich"
],
"health_check": {
"endpoint": "localhost:5432",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "immich-postgres",
"name": "Immich Postgres",
"ports": [],
"resources": {
"disk_limit": "40Gi",
"memory_limit": "2Gi"
},
"security": {
"capabilities": [
"CHOWN",
"DAC_OVERRIDE",
"FOWNER",
"SETGID",
"SETUID"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "14-vectorchord0.4.3-pgvectors0.2.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/immich-db",
"target": "/var/lib/postgresql/data",
"type": "bind"
}
]
}
},
"version": "14-vectorchord0.4.3-pgvectors0.2.0"
},
"immich-redis": {
"manifest": {
"app": {
"container": {
"image": "146.59.87.168:3000/lfg2025/valkey:7-alpine",
"network": "archy-net",
"pull_policy": "if-not-present"
},
"container_name": "immich_redis",
"dependencies": [],
"description": "Valkey (Redis-compatible) cache for Immich.",
"environment": [],
"health_check": {
"endpoint": "localhost:6379",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "immich-redis",
"name": "Immich Redis",
"ports": [],
"resources": {
"memory_limit": "128Mi"
},
"security": {
"capabilities": [
"SETGID",
"SETUID"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "7-alpine",
"volumes": []
}
},
"version": "7-alpine"
},
"indeedhub": {
"image": "146.59.87.168:3000/lfg2025/indeedhub:1.0.0",
"images": {
"indeedhub": "146.59.87.168:3000/lfg2025/indeedhub:1.0.0",
"indeedhub-api": "146.59.87.168:3000/lfg2025/indeedhub-api:1.0.0",
"indeedhub-ffmpeg": "146.59.87.168:3000/lfg2025/indeedhub-ffmpeg:1.0.0"
},
"manifest": {
"app": {
"category": "community",
"container": {
"image": "146.59.87.168:3000/lfg2025/indeedhub:1.0.0",
"network": "indeedhub-net",
"pull_policy": "if-not-present"
},
"container_name": "indeedhub",
"dependencies": [
{
"app_id": "indeedhub-api"
},
{
"storage": "1Gi"
}
],
"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.",
"environment": [],
"health_check": {
"endpoint": "localhost:7777",
"interval": "30s",
"retries": 5,
"start_period": "30s",
"timeout": "5s",
"type": "tcp"
},
"hooks": {
"post_install": [
{
"exec": [
"sed",
"-i",
"/X-Frame-Options/d",
"/etc/nginx/conf.d/default.conf"
]
},
{
"copy_from_host": {
"dest": "/usr/share/nginx/html/nostr-provider.js",
"src": "web-ui/nostr-provider.js"
}
},
{
"exec": [
"sh",
"-c",
"grep -q nostr-provider /etc/nginx/conf.d/default.conf || sed -i 's#</head>#<script src=\"/nostr-provider.js\"></script></head>#' /etc/nginx/conf.d/default.conf"
]
},
{
"exec": [
"nginx",
"-s",
"reload"
]
}
]
},
"id": "indeedhub",
"interfaces": {
"main": {
"description": "Stream Bitcoin documentaries with Nostr identity",
"name": "Web UI",
"path": "/",
"port": 7778,
"protocol": "http",
"type": "ui"
}
},
"metadata": {
"author": "Indeehub Team",
"icon": "/assets/img/app-icons/indeedhub.png",
"license": "MIT",
"repo": "https://github.com/indeedhub/indeedhub",
"tags": [
"bitcoin",
"documentary",
"streaming",
"media",
"education",
"nostr"
],
"website": "https://indeedhub.com"
},
"name": "IndeeHub",
"ports": [
{
"container": 7777,
"host": 7778,
"protocol": "tcp"
}
],
"resources": {
"disk_limit": "1Gi",
"memory_limit": "512Mi"
},
"security": {
"capabilities": [
"CHOWN",
"DAC_OVERRIDE",
"SETGID",
"SETUID"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "1.0.0",
"volumes": [
{
"options": [
"rw",
"nosuid",
"nodev",
"size=16m"
],
"target": "/run",
"type": "tmpfs"
},
{
"options": [
"rw",
"nosuid",
"nodev",
"size=32m"
],
"target": "/var/cache/nginx",
"type": "tmpfs"
}
]
}
},
"version": "1.0.0"
},
"indeedhub-api": {
"manifest": {
"app": {
"category": "community",
"container": {
"generated_secrets": [
{
"kind": "hex32",
"name": "indeedhub-jwt"
}
],
"image": "146.59.87.168:3000/lfg2025/indeedhub-api:1.0.0",
"network": "indeedhub-net",
"network_aliases": [
"api"
],
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "DATABASE_PASSWORD",
"secret_file": "indeedhub-db-password"
},
{
"key": "AWS_SECRET_KEY",
"secret_file": "indeedhub-minio-password"
},
{
"key": "NOSTR_JWT_SECRET",
"secret_file": "indeedhub-jwt"
}
]
},
"container_name": "indeedhub-api",
"dependencies": [
{
"app_id": "indeedhub-postgres"
},
{
"app_id": "indeedhub-redis"
},
{
"app_id": "indeedhub-minio"
}
],
"description": "IndeedHub backend API (Nostr auth, media, payments).",
"environment": [
"PORT=4000",
"DATABASE_HOST=postgres",
"DATABASE_PORT=5432",
"DATABASE_USER=indeedhub",
"DATABASE_NAME=indeedhub",
"QUEUE_HOST=redis",
"QUEUE_PORT=6379",
"S3_ENDPOINT=http://minio:9000",
"AWS_REGION=us-east-1",
"AWS_ACCESS_KEY=indeeadmin",
"S3_PUBLIC_BUCKET_NAME=indeedhub-public",
"S3_PRIVATE_BUCKET_NAME=indeedhub-private",
"S3_PUBLIC_BUCKET_URL=/storage",
"NOSTR_JWT_EXPIRES_IN=7d",
"AES_MASTER_SECRET=0123456789abcdef0123456789abcdef",
"ENVIRONMENT=production"
],
"health_check": {
"endpoint": "localhost:4000",
"interval": "30s",
"retries": 10,
"timeout": "5s",
"type": "tcp"
},
"id": "indeedhub-api",
"name": "IndeedHub API",
"ports": [],
"resources": {
"memory_limit": "2Gi"
},
"security": {
"capabilities": [],
"network_policy": "isolated",
"readonly_root": false
},
"version": "1.0.0",
"volumes": []
}
},
"version": "1.0.0"
},
"indeedhub-ffmpeg": {
"manifest": {
"app": {
"category": "community",
"container": {
"image": "146.59.87.168:3000/lfg2025/indeedhub-ffmpeg:1.0.0",
"network": "indeedhub-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "DATABASE_PASSWORD",
"secret_file": "indeedhub-db-password"
},
{
"key": "AWS_SECRET_KEY",
"secret_file": "indeedhub-minio-password"
}
]
},
"container_name": "indeedhub-ffmpeg",
"dependencies": [
{
"app_id": "indeedhub-api"
}
],
"description": "IndeedHub background media transcoding worker.",
"environment": [
"DATABASE_HOST=postgres",
"DATABASE_PORT=5432",
"DATABASE_USER=indeedhub",
"DATABASE_NAME=indeedhub",
"QUEUE_HOST=redis",
"QUEUE_PORT=6379",
"S3_ENDPOINT=http://minio:9000",
"AWS_REGION=us-east-1",
"AWS_ACCESS_KEY=indeeadmin",
"S3_PUBLIC_BUCKET_NAME=indeedhub-public",
"S3_PRIVATE_BUCKET_NAME=indeedhub-private",
"ENVIRONMENT=production",
"AES_MASTER_SECRET=0123456789abcdef0123456789abcdef"
],
"id": "indeedhub-ffmpeg",
"name": "IndeedHub FFmpeg Worker",
"ports": [],
"resources": {
"memory_limit": "4Gi"
},
"security": {
"capabilities": [],
"network_policy": "isolated",
"readonly_root": false
},
"version": "1.0.0",
"volumes": []
}
},
"version": "1.0.0"
},
"indeedhub-minio": {
"manifest": {
"app": {
"category": "community",
"container": {
"custom_args": [
"server",
"/data"
],
"generated_secrets": [
{
"kind": "hex32",
"name": "indeedhub-minio-password"
}
],
"image": "146.59.87.168:3000/lfg2025/minio:RELEASE.2024-11-07T00-52-20Z",
"network": "indeedhub-net",
"network_aliases": [
"minio"
],
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "MINIO_ROOT_PASSWORD",
"secret_file": "indeedhub-minio-password"
}
]
},
"container_name": "indeedhub-minio",
"dependencies": [
{
"storage": "50Gi"
}
],
"description": "MinIO S3-compatible object storage for IndeedHub media.",
"environment": [
"MINIO_ROOT_USER=indeeadmin"
],
"health_check": {
"endpoint": "http://localhost:9000",
"interval": "30s",
"path": "/minio/health/live",
"retries": 5,
"timeout": "5s",
"type": "http"
},
"id": "indeedhub-minio",
"name": "IndeedHub MinIO",
"ports": [],
"resources": {
"disk_limit": "50Gi",
"memory_limit": "1Gi"
},
"security": {
"capabilities": [],
"network_policy": "isolated",
"readonly_root": false
},
"version": "RELEASE.2024-11-07T00-52-20Z",
"volumes": [
{
"options": [
"rw"
],
"source": "indeedhub-minio-data",
"target": "/data",
"type": "volume"
}
]
}
},
"version": "RELEASE.2024-11-07T00-52-20Z"
},
"indeedhub-postgres": {
"manifest": {
"app": {
"category": "community",
"container": {
"generated_secrets": [
{
"kind": "hex32",
"name": "indeedhub-db-password"
}
],
"image": "146.59.87.168:3000/lfg2025/postgres:16.13-alpine",
"network": "indeedhub-net",
"network_aliases": [
"postgres"
],
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "POSTGRES_PASSWORD",
"secret_file": "indeedhub-db-password"
}
]
},
"container_name": "indeedhub-postgres",
"dependencies": [
{
"storage": "10Gi"
}
],
"description": "Postgres database backend for IndeedHub.",
"environment": [
"POSTGRES_USER=indeedhub",
"POSTGRES_DB=indeedhub"
],
"health_check": {
"endpoint": "localhost:5432",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "indeedhub-postgres",
"name": "IndeedHub Postgres",
"ports": [],
"resources": {
"disk_limit": "10Gi",
"memory_limit": "1Gi"
},
"security": {
"capabilities": [
"CHOWN",
"DAC_OVERRIDE",
"FOWNER",
"SETGID",
"SETUID"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "16.13-alpine",
"volumes": [
{
"options": [
"rw"
],
"source": "indeedhub-postgres-data",
"target": "/var/lib/postgresql/data",
"type": "volume"
}
]
}
},
"version": "16.13-alpine"
},
"indeedhub-redis": {
"manifest": {
"app": {
"category": "community",
"container": {
"image": "146.59.87.168:3000/lfg2025/redis:7.4.8-alpine",
"network": "indeedhub-net",
"network_aliases": [
"redis"
],
"pull_policy": "if-not-present"
},
"container_name": "indeedhub-redis",
"dependencies": [
{
"storage": "1Gi"
}
],
"description": "Redis queue/cache backend for IndeedHub.",
"environment": [],
"health_check": {
"endpoint": "localhost:6379",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "indeedhub-redis",
"name": "IndeedHub Redis",
"ports": [],
"resources": {
"memory_limit": "256Mi"
},
"security": {
"capabilities": [
"SETGID",
"SETUID"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "7.4.8-alpine",
"volumes": [
{
"options": [
"rw"
],
"source": "indeedhub-redis-data",
"target": "/data",
"type": "volume"
}
]
}
},
"version": "7.4.8-alpine"
},
"indeedhub-relay": {
"manifest": {
"app": {
"category": "community",
"container": {
"image": "146.59.87.168:3000/lfg2025/nostr-rs-relay:0.9.0",
"network": "indeedhub-net",
"network_aliases": [
"relay"
],
"pull_policy": "if-not-present"
},
"container_name": "indeedhub-relay",
"dependencies": [
{
"storage": "2Gi"
}
],
"description": "nostr-rs-relay backing IndeedHub's Nostr identity + comments.",
"environment": [],
"health_check": {
"endpoint": "localhost:8080",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "indeedhub-relay",
"name": "IndeedHub Nostr Relay",
"ports": [],
"resources": {
"disk_limit": "2Gi",
"memory_limit": "256Mi"
},
"security": {
"capabilities": [],
"network_policy": "isolated",
"readonly_root": false
},
"version": "0.9.0",
"volumes": [
{
"options": [
"rw"
],
"source": "indeedhub-relay-data",
"target": "/usr/src/app/db",
"type": "volume"
}
]
}
},
"version": "0.9.0"
},
"jellyfin": {
"image": "146.59.87.168:3000/lfg2025/jellyfin:10.8.13",
"manifest": {
"app": {
"container": {
"image": "146.59.87.168:3000/lfg2025/jellyfin:10.8.13",
"network": "pasta",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "10Gi"
}
],
"description": "Free media server. Stream movies, music, and photos.",
"environment": [],
"health_check": {
"endpoint": "localhost:8096",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "jellyfin",
"interfaces": {
"main": {
"description": "Jellyfin media dashboard",
"name": "Web UI",
"path": "/",
"port": 8096,
"protocol": "http",
"type": "ui"
}
},
"metadata": {
"author": "Jellyfin",
"category": "data",
"icon": "/assets/img/app-icons/jellyfin.webp",
"repo": "https://github.com/jellyfin/jellyfin"
},
"name": "Jellyfin",
"ports": [
{
"container": 8096,
"host": 8096,
"protocol": "tcp"
}
],
"resources": {
"disk_limit": "10Gi",
"memory_limit": "1Gi"
},
"security": {
"capabilities": [
"CHOWN",
"FOWNER",
"SETUID",
"SETGID",
"DAC_OVERRIDE"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "10.8.13",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/jellyfin/config",
"target": "/config",
"type": "bind"
},
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/jellyfin/cache",
"target": "/cache",
"type": "bind"
}
]
}
},
"version": "10.8.13"
},
"lightning-stack": {
"manifest": {
"app": {
"bitcoin_integration": {
"rpc_access": "admin",
"sync_required": true
},
"container": {
"image": "lightninglabs/lightning-stack:v0.12.0",
"image_signature": "cosign://...",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"app_id": "bitcoin-core",
"version": ">=24.0"
},
{
"storage": "50Gi"
}
],
"description": "Complete Lightning Network implementation. Includes LND, CLN, and management tools.",
"environment": [
"BITCOIND_HOST=bitcoin-core",
"BITCOIND_RPCUSER=${BITCOIN_RPC_USER}",
"BITCOIND_RPCPASS=${BITCOIN_RPC_PASSWORD}",
"NETWORK=mainnet"
],
"health_check": {
"endpoint": "http://127.0.0.1:8080",
"interval": "30s",
"path": "/v1/getinfo",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "lightning-stack",
"lightning_integration": {
"channel_management": true,
"payment_routing": true
},
"name": "Lightning Stack",
"ports": [
{
"container": 9735,
"host": 9738,
"protocol": "tcp"
},
{
"container": 10009,
"host": 10010,
"protocol": "tcp"
},
{
"container": 8080,
"host": 8091,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 4,
"disk_limit": "50Gi",
"memory_limit": "4Gi"
},
"security": {
"apparmor_profile": "lightning-stack",
"capabilities": [
"NET_BIND_SERVICE"
],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": true,
"seccomp_profile": "default",
"user": 1000
},
"version": "0.12.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/lightning-stack",
"target": "/root/.lightning",
"type": "bind"
}
]
}
},
"version": "0.12.0"
},
"lnd": {
"image": "146.59.87.168:3000/lfg2025/lnd:v0.18.4-beta",
"manifest": {
"app": {
"bitcoin_integration": {
"rpc_access": "admin",
"sync_required": true
},
"container": {
"data_uid": "100000:100000",
"derived_env": [
{
"key": "BITCOIND_HOST",
"template": "{{BITCOIN_HOST}}"
}
],
"image": "146.59.87.168:3000/lfg2025/lnd:v0.18.4-beta",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "BITCOIND_RPCPASS",
"secret_file": "bitcoin-rpc-password"
}
]
},
"dependencies": [
{
"app_id": "bitcoin-core",
"version": ">=26.0"
}
],
"description": "Lightning Network implementation by Lightning Labs. Enables instant, low-cost Bitcoin payments.",
"environment": [
"BITCOIND_RPCUSER=archipelago",
"NETWORK=mainnet"
],
"health_check": {
"endpoint": "localhost:10009",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "lnd",
"lightning_integration": {
"channel_management": true,
"payment_routing": true
},
"name": "LND",
"ports": [
{
"container": 9735,
"host": 9735,
"protocol": "tcp"
},
{
"container": 10009,
"host": 10009,
"protocol": "tcp"
},
{
"container": 8080,
"host": 18080,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 2,
"disk_limit": "10Gi",
"memory_limit": "1Gi"
},
"security": {
"capabilities": [
"CHOWN",
"FOWNER",
"SETUID",
"SETGID",
"DAC_OVERRIDE",
"NET_RAW"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "0.18.4",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/lnd",
"target": "/root/.lnd",
"type": "bind"
}
]
}
},
"version": "v0.18.4-beta"
},
"lnd-ui": {
"image": "146.59.87.168:3000/lfg2025/lnd-ui:latest",
"manifest": {
"app": {
"container": {
"build": {
"context": "/opt/archipelago/docker/lnd-ui",
"dockerfile": "Dockerfile",
"tag": "localhost/lnd-ui:local"
}
},
"dependencies": [
{
"app_id": "lnd"
}
],
"description": "Archipelago-native HTTP frontend for LND. Runs nginx inside a\ncontainer and serves static assets. LND connection info is fetched\nvia an absolute URL that the host nginx routes to the archipelago\nbackend on 127.0.0.1:5678, so no upstream auth is baked in.\n",
"environment": [],
"health_check": {
"endpoint": "http://127.0.0.1:18083",
"interval": "30s",
"path": "/",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "lnd-ui",
"name": "LND UI",
"ports": [
{
"container": 80,
"host": 18083,
"protocol": "tcp"
}
],
"resources": {
"memory_limit": "64Mi"
},
"security": {
"network_policy": "bridge",
"readonly_root": false
},
"version": "1.0.0",
"volumes": []
}
},
"version": "latest"
},
"mempool": {
"image": "146.59.87.168:3000/lfg2025/mempool-frontend:v3.0.1",
"images": {
"archy-mempool-db": "146.59.87.168:3000/lfg2025/mariadb:11.4.10",
"archy-mempool-web": "146.59.87.168:3000/lfg2025/mempool-frontend:v3.0.1",
"mempool-api": "146.59.87.168:3000/lfg2025/mempool-backend:v3.0.0"
},
"manifest": {
"app": {
"bitcoin_integration": {
"rpc_access": "read-only",
"sync_required": true
},
"container": {
"image": "146.59.87.168:3000/lfg2025/mempool-frontend:v3.0.1",
"image_signature": "cosign://...",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"app_id": "bitcoin-core",
"version": ">=24.0"
},
{
"storage": "20Gi"
},
"bitcoin:archival"
],
"description": "Bitcoin mempool and blockchain explorer. Real-time transaction and block visualization.",
"environment": [
"MEMPOOL_BACKEND=electrum",
"MEMPOOL_BITCOIN_HOST=bitcoin-core",
"MEMPOOL_BITCOIN_PORT=8332",
"MEMPOOL_BITCOIN_USER=${BITCOIN_RPC_USER}",
"MEMPOOL_BITCOIN_PASSWORD=${BITCOIN_RPC_PASSWORD}"
],
"health_check": {
"endpoint": "http://localhost:4080",
"interval": "30s",
"path": "/api/health",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "mempool",
"name": "Mempool Explorer",
"ports": [
{
"container": 8080,
"host": 4080,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 2,
"disk_limit": "20Gi",
"memory_limit": "2Gi"
},
"security": {
"apparmor_profile": "mempool",
"capabilities": [],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": true,
"seccomp_profile": "default",
"user": 1000
},
"version": "3.0.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/mempool",
"target": "/data",
"type": "bind"
}
]
}
},
"version": "v3.0.1"
},
"mempool-api": {
"manifest": {
"app": {
"bitcoin_integration": {
"pruning_support": false,
"rpc_access": "read-only",
"sync_required": true
},
"container": {
"derived_env": [
{
"key": "CORE_RPC_HOST",
"template": "{{BITCOIN_HOST}}"
}
],
"image": "146.59.87.168:3000/lfg2025/mempool-backend:v3.0.0",
"network": "archy-net",
"pull_policy": "if-not-present",
"secret_env": [
{
"key": "CORE_RPC_PASSWORD",
"secret_file": "bitcoin-rpc-password"
},
{
"key": "DATABASE_PASSWORD",
"secret_file": "mempool-db-password"
}
]
},
"dependencies": [
{
"app_id": "bitcoin-knots",
"version": ">=26.0"
},
{
"app_id": "electrumx",
"version": ">=1.18.0"
},
{
"app_id": "archy-mempool-db",
"version": ">=11.4.10"
},
"bitcoin:archival"
],
"description": "Backend API for mempool explorer.",
"environment": [
"MEMPOOL_BACKEND=electrum",
"ELECTRUM_HOST=electrumx",
"ELECTRUM_PORT=50001",
"ELECTRUM_TLS_ENABLED=false",
"CORE_RPC_PORT=8332",
"CORE_RPC_USERNAME=archipelago",
"DATABASE_ENABLED=true",
"DATABASE_HOST=archy-mempool-db",
"DATABASE_DATABASE=mempool",
"DATABASE_USERNAME=mempool"
],
"health_check": {
"endpoint": "http://localhost:8999",
"interval": "30s",
"path": "/api/v1/backend-info",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "mempool-api",
"name": "Mempool API",
"ports": [
{
"container": 8999,
"host": 8999,
"protocol": "tcp"
}
],
"resources": {
"disk_limit": "20Gi",
"memory_limit": "2Gi"
},
"security": {
"capabilities": [],
"network_policy": "isolated",
"readonly_root": false
},
"version": "3.0.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/mempool",
"target": "/data",
"type": "bind"
}
]
}
},
"version": "3.0.0"
},
"morphos-server": {
"manifest": {
"app": {
"container": {
"image": "archipelago/morphos-server:1.0.0",
"image_signature": "cosign://...",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "5Gi"
}
],
"description": "MorphOS server platform. Decentralized application server.",
"environment": [
"MORPHOS_ENV=production",
"MORPHOS_DATA_DIR=/app/data"
],
"health_check": {
"endpoint": "http://127.0.0.1:8080",
"interval": "30s",
"path": "/health",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "morphos-server",
"name": "MorphOS Server",
"ports": [
{
"container": 8080,
"host": 8089,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 2,
"disk_limit": "5Gi",
"memory_limit": "2Gi"
},
"security": {
"apparmor_profile": "morphos-server",
"capabilities": [],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": true,
"seccomp_profile": "default",
"user": 1000
},
"version": "1.0.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/morphos-server",
"target": "/app/data",
"type": "bind"
}
]
}
},
"version": "1.0.0"
},
"netbird": {
"manifest": {
"app": {
"category": "networking",
"container": {
"generated_certs": [
{
"crt": "/var/lib/archipelago/netbird/tls.crt",
"key": "/var/lib/archipelago/netbird/tls.key"
}
],
"image": "docker.io/library/nginx:1.27-alpine",
"network": "netbird-net",
"pull_policy": "if-not-present"
},
"container_name": "netbird",
"dependencies": [
{
"app_id": "netbird-server"
},
{
"app_id": "netbird-dashboard"
},
{
"storage": "1Gi"
}
],
"description": "Self-hosted WireGuard mesh VPN control plane with dashboard, embedded identity provider, management API, signal, relay, and STUN. The user-facing entry point — a TLS proxy in front of the dashboard + server.",
"environment": [],
"files": [
{
"content": "server {\n listen 443 ssl;\n server_name _;\n\n # netbird's dashboard needs a secure context (window.crypto.subtle for\n # OIDC PKCE), so the proxy terminates TLS with a self-signed cert (#15).\n ssl_certificate /etc/nginx/tls.crt;\n ssl_certificate_key /etc/nginx/tls.key;\n\n # Rootless Podman can hand a container a new IP across restarts/reboots.\n # nginx resolves a literal upstream name ONCE at startup and caches it,\n # so after the IP moves every request 502s with \"host unreachable\"\n # (issue #15, observed live on .198: nginx pinned to a dead\n # netbird-dashboard IP). Fix: point `resolver` at the netbird-net\n # gateway (Podman's aardvark DNS) and use VARIABLE upstreams, which\n # forces nginx to re-resolve the container names at request time.\n resolver {{NETWORK_GATEWAY}} valid=10s ipv6=off;\n\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_http_version 1.1;\n\n location ~ ^/(relay|ws-proxy/) {\n set $nb_server netbird-server;\n proxy_pass http://$nb_server:80;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection \"upgrade\";\n proxy_read_timeout 1d;\n }\n\n location ~ ^/(api|oauth2)(/|$) {\n # The dashboard is a SPA whose API/OIDC base URL is baked at build\n # time to one host:port. A single box is reached via several\n # addresses, so those fetches are cross-origin and the browser\n # blocks them with no Access-Control-Allow-Origin (#15, live on\n # .198). Reflect the caller's Origin and answer the CORS preflight.\n if ($request_method = OPTIONS) {\n add_header Access-Control-Allow-Origin $http_origin always;\n add_header Access-Control-Allow-Credentials true always;\n add_header Access-Control-Allow-Methods \"GET, POST, PUT, PATCH, DELETE, OPTIONS\" always;\n add_header Access-Control-Allow-Headers \"Authorization, Content-Type, Accept\" always;\n add_header Access-Control-Max-Age 86400 always;\n add_header Content-Length 0;\n return 204;\n }\n add_header Access-Control-Allow-Origin $http_origin always;\n add_header Access-Control-Allow-Credentials true always;\n add_header Access-Control-Allow-Methods \"GET, POST, PUT, PATCH, DELETE, OPTIONS\" always;\n add_header Access-Control-Allow-Headers \"Authorization, Content-Type, Accept\" always;\n set $nb_server netbird-server;\n proxy_pass http://$nb_server:80;\n }\n\n location ~ ^/(signalexchange\\.SignalExchange|management\\.ManagementService|management\\.ProxyService)/ {\n set $nb_server netbird-server;\n grpc_pass grpc://$nb_server:80;\n grpc_read_timeout 1d;\n grpc_send_timeout 1d;\n }\n\n # OIDC callback routes are client-side SPA routes with NO prebuilt page\n # in the dashboard bundle, so proxying them straight through 404s —\n # which crashes the dashboard's auth init and shows \"Unauthenticated\"\n # with dead buttons (#15, live on .198: /nb-auth + /nb-silent-auth\n # returned 404). Serve index.html at these paths (URL unchanged) so\n # react-oidc boots and completes the login / silent-SSO.\n location ~ ^/(nb-auth|nb-silent-auth) {\n set $nb_dashboard netbird-dashboard;\n rewrite ^.*$ /index.html break;\n proxy_pass http://$nb_dashboard:80;\n }\n\n location / {\n set $nb_dashboard netbird-dashboard;\n proxy_pass http://$nb_dashboard:80;\n }\n}\n",
"overwrite": true,
"path": "/var/lib/archipelago/netbird/nginx.conf"
}
],
"health_check": {
"endpoint": "localhost:443",
"interval": "30s",
"retries": 5,
"start_period": "20s",
"timeout": "5s",
"type": "tcp"
},
"id": "netbird",
"interfaces": {
"main": {
"description": "Manage your self-hosted NetBird mesh VPN",
"name": "Dashboard",
"path": "/",
"port": 8087,
"protocol": "https",
"type": "ui"
}
},
"metadata": {
"author": "NetBird",
"icon": "/assets/img/app-icons/netbird.svg",
"license": "BSD-3-Clause",
"repo": "https://github.com/netbirdio/netbird",
"tags": [
"networking",
"vpn",
"wireguard",
"mesh"
],
"website": "https://netbird.io"
},
"name": "NetBird",
"ports": [
{
"container": 443,
"host": 8087,
"protocol": "tcp"
}
],
"resources": {
"memory_limit": "256Mi"
},
"security": {
"capabilities": [
"CHOWN",
"DAC_OVERRIDE",
"SETGID",
"SETUID",
"NET_BIND_SERVICE"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "2.38.0",
"volumes": [
{
"options": [
"ro"
],
"source": "/var/lib/archipelago/netbird/nginx.conf",
"target": "/etc/nginx/conf.d/default.conf",
"type": "bind"
},
{
"options": [
"ro"
],
"source": "/var/lib/archipelago/netbird/tls.crt",
"target": "/etc/nginx/tls.crt",
"type": "bind"
},
{
"options": [
"ro"
],
"source": "/var/lib/archipelago/netbird/tls.key",
"target": "/etc/nginx/tls.key",
"type": "bind"
}
]
}
},
"version": "2.38.0"
},
"netbird-dashboard": {
"manifest": {
"app": {
"category": "networking",
"container": {
"derived_env": [
{
"key": "NETBIRD_MGMT_API_ENDPOINT",
"template": "https://{{HOST_IP}}:8087"
},
{
"key": "NETBIRD_MGMT_GRPC_API_ENDPOINT",
"template": "https://{{HOST_IP}}:8087"
},
{
"key": "AUTH_AUTHORITY",
"template": "https://{{HOST_IP}}:8087/oauth2"
}
],
"image": "docker.io/netbirdio/dashboard:v2.38.0",
"network": "netbird-net",
"network_aliases": [
"netbird-dashboard"
],
"pull_policy": "if-not-present"
},
"container_name": "netbird-dashboard",
"dependencies": [
{
"app_id": "netbird-server"
}
],
"description": "NetBird management dashboard (SPA). Internal stack member served through the netbird proxy.",
"environment": [
"AUTH_AUDIENCE=netbird-dashboard",
"AUTH_CLIENT_ID=netbird-dashboard",
"AUTH_CLIENT_SECRET=",
"USE_AUTH0=false",
"AUTH_SUPPORTED_SCOPES=openid profile email groups",
"AUTH_REDIRECT_URI=/nb-auth",
"AUTH_SILENT_REDIRECT_URI=/nb-silent-auth",
"NETBIRD_TOKEN_SOURCE=idToken",
"NGINX_SSL_PORT=443",
"LETSENCRYPT_DOMAIN=none"
],
"health_check": {
"endpoint": "localhost:80",
"interval": "30s",
"retries": 5,
"start_period": "20s",
"timeout": "5s",
"type": "tcp"
},
"id": "netbird-dashboard",
"metadata": {
"author": "NetBird",
"icon": "/assets/img/app-icons/netbird.svg",
"license": "BSD-3-Clause",
"repo": "https://github.com/netbirdio/dashboard",
"tags": [
"networking",
"vpn",
"dashboard"
],
"website": "https://netbird.io"
},
"name": "NetBird Dashboard",
"ports": [],
"resources": {
"memory_limit": "256Mi"
},
"security": {
"capabilities": [
"CHOWN",
"DAC_OVERRIDE",
"SETGID",
"SETUID",
"NET_BIND_SERVICE"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "2.38.0",
"volumes": []
}
},
"version": "2.38.0"
},
"netbird-server": {
"manifest": {
"app": {
"category": "networking",
"container": {
"custom_args": [
"--config",
"/etc/netbird/config.yaml"
],
"generated_secrets": [
{
"kind": "base64",
"name": "netbird-relay-auth-secret"
},
{
"kind": "base64",
"name": "netbird-store-encryption-key"
}
],
"image": "docker.io/netbirdio/netbird-server:0.71.2",
"network": "netbird-net",
"network_aliases": [
"netbird-server"
],
"pull_policy": "if-not-present"
},
"container_name": "netbird-server",
"dependencies": [
{
"storage": "1Gi"
}
],
"description": "NetBird combined management / signal / relay server with an embedded identity provider and STUN. Backend for the self-hosted NetBird mesh VPN.",
"environment": [],
"files": [
{
"content": "server:\n listenAddress: \":80\"\n exposedAddress: \"https://{{HOST_IP}}:8087\"\n stunPorts:\n - 3478\n metricsPort: 9090\n healthcheckAddress: \":9000\"\n logLevel: \"info\"\n logFile: \"console\"\n authSecret: \"{{secret:netbird-relay-auth-secret}}\"\n dataDir: \"/var/lib/netbird\"\n auth:\n issuer: \"https://{{HOST_IP}}:8087/oauth2\"\n localAuthDisabled: false\n signKeyRefreshEnabled: false\n dashboardRedirectURIs:\n - \"https://{{HOST_IP}}:8087/nb-auth\"\n - \"https://{{HOST_IP}}:8087/nb-silent-auth\"\n dashboardPostLogoutRedirectURIs:\n - \"https://{{HOST_IP}}:8087/\"\n cliRedirectURIs:\n - \"http://localhost:53000/\"\n store:\n engine: \"sqlite\"\n encryptionKey: \"{{secret:netbird-store-encryption-key}}\"\n",
"overwrite": true,
"path": "/var/lib/archipelago/netbird/config.yaml"
}
],
"health_check": {
"endpoint": "localhost:80",
"interval": "30s",
"retries": 10,
"start_period": "30s",
"timeout": "5s",
"type": "tcp"
},
"id": "netbird-server",
"metadata": {
"author": "NetBird",
"icon": "/assets/img/app-icons/netbird.svg",
"license": "BSD-3-Clause",
"repo": "https://github.com/netbirdio/netbird",
"tags": [
"networking",
"vpn",
"wireguard",
"mesh"
],
"website": "https://netbird.io"
},
"name": "NetBird Server",
"ports": [
{
"container": 80,
"host": 8086,
"protocol": "tcp"
},
{
"container": 3478,
"host": 3478,
"protocol": "udp"
}
],
"resources": {
"memory_limit": "1Gi"
},
"security": {
"capabilities": [
"NET_BIND_SERVICE"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "0.71.2",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/netbird/data",
"target": "/var/lib/netbird",
"type": "bind"
},
{
"options": [
"ro"
],
"source": "/var/lib/archipelago/netbird/config.yaml",
"target": "/etc/netbird/config.yaml",
"type": "bind"
}
]
}
},
"version": "0.71.2"
},
"nextcloud": {
"image": "146.59.87.168:3000/lfg2025/nextcloud:29",
"manifest": {
"app": {
"container": {
"image": "146.59.87.168:3000/lfg2025/nextcloud:29",
"network": "pasta",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "10Gi"
}
],
"description": "Your own private cloud. File sync, calendars, contacts.",
"environment": [],
"health_check": {
"endpoint": "localhost:80",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "nextcloud",
"interfaces": {
"main": {
"description": "Nextcloud file and collaboration dashboard",
"name": "Web UI",
"path": "/",
"port": 8085,
"protocol": "http",
"type": "ui"
}
},
"metadata": {
"author": "Nextcloud",
"category": "data",
"icon": "/assets/img/app-icons/nextcloud.webp",
"launch": {
"open_in_new_tab": true
},
"repo": "https://github.com/nextcloud/server"
},
"name": "Nextcloud",
"ports": [
{
"container": 80,
"host": 8085,
"protocol": "tcp"
}
],
"resources": {
"disk_limit": "10Gi",
"memory_limit": "1Gi"
},
"security": {
"capabilities": [
"CHOWN",
"SETUID",
"SETGID",
"DAC_OVERRIDE",
"NET_BIND_SERVICE"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "29",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/nextcloud",
"target": "/var/www/html",
"type": "bind"
}
]
}
},
"version": "29"
},
"nginx-proxy-manager": {
"image": "146.59.87.168:3000/lfg2025/nginx-proxy-manager:latest",
"version": "latest"
},
"nostr-rs-relay": {
"image": "146.59.87.168:3000/lfg2025/nostr-rs-relay:0.9.0",
"manifest": {
"app": {
"container": {
"data_uid": "1000:1000",
"image": "scsibug/nostr-rs-relay:0.8.9",
"image_signature": "cosign://...",
"pull_policy": "verify-signature"
},
"dependencies": [
{
"storage": "10Gi"
}
],
"description": "High-performance Nostr relay written in Rust. Host your own decentralized social media relay and earn networking profits.",
"environment": [
"RELAY_NAME=Archipelago Nostr Relay",
"RELAY_DESCRIPTION=Self-hosted Nostr relay on Archipelago",
"MAX_EVENTS=1000000",
"MAX_SUBSCRIPTIONS=100"
],
"health_check": {
"endpoint": "http://localhost:8080",
"interval": "30s",
"path": "/",
"retries": 5,
"timeout": "30s",
"type": "http"
},
"id": "nostr-rs-relay",
"name": "Nostr Relay (Rust)",
"nostr_integration": {
"event_storage": "sqlite",
"monetization_enabled": true,
"relay_type": "public"
},
"ports": [
{
"container": 8080,
"host": 18081,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 2,
"disk_limit": "10Gi",
"memory_limit": "1Gi"
},
"security": {
"apparmor_profile": "nostr-relay",
"capabilities": [],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": true,
"seccomp_profile": "default",
"user": 1000
},
"version": "0.8.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/nostr-relay",
"target": "/usr/src/app/db",
"type": "bind"
}
]
}
},
"version": "0.9.0"
},
"nostr-vpn": {
"image": "146.59.87.168:3000/lfg2025/nostr-vpn:v0.3.7",
"version": "v0.3.7"
},
"ollama": {
"image": "146.59.87.168:3000/lfg2025/ollama:latest",
"version": "latest"
},
"penpot": {
"image": "146.59.87.168:3000/lfg2025/penpot-frontend:2.4",
"images": {
"penpot-backend": "146.59.87.168:3000/lfg2025/penpot-backend:2.4",
"penpot-exporter": "146.59.87.168:3000/lfg2025/penpot-exporter:2.4",
"penpot-frontend": "146.59.87.168:3000/lfg2025/penpot-frontend:2.4",
"penpot-postgres": "146.59.87.168:3000/lfg2025/postgres:15",
"penpot-valkey": "146.59.87.168:3000/lfg2025/valkey:8.1"
},
"version": "2.4"
},
"photoprism": {
"image": "146.59.87.168:3000/lfg2025/photoprism:240915",
"manifest": {
"app": {
"container": {
"image": "146.59.87.168:3000/lfg2025/photoprism:240915",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "10Gi"
}
],
"description": "AI-powered photo management with facial recognition.",
"environment": [
"PHOTOPRISM_ADMIN_PASSWORD=archipelago",
"PHOTOPRISM_DEFAULT_LOCALE=en"
],
"health_check": {
"endpoint": "localhost:2342",
"interval": "60s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "photoprism",
"interfaces": {
"main": {
"description": "PhotoPrism photo library",
"name": "Web UI",
"path": "/",
"port": 2342,
"protocol": "http",
"type": "ui"
}
},
"metadata": {
"author": "PhotoPrism",
"category": "data",
"icon": "/assets/img/app-icons/photoprism.svg",
"launch": {
"open_in_new_tab": true
},
"repo": "https://github.com/photoprism/photoprism"
},
"name": "PhotoPrism",
"ports": [
{
"container": 2342,
"host": 2342,
"protocol": "tcp"
}
],
"resources": {
"disk_limit": "10Gi",
"memory_limit": "1Gi"
},
"security": {
"capabilities": [
"CHOWN",
"SETUID",
"SETGID"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "240915",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/photoprism",
"target": "/photoprism/storage",
"type": "bind"
}
]
}
},
"version": "240915"
},
"pine": {
"manifest": {
"app": {
"category": "home",
"container": {
"generated_certs": [
{
"crt": "/var/lib/archipelago/pine/tls.crt",
"key": "/var/lib/archipelago/pine/tls.key"
}
],
"image": "docker.io/library/nginx:1.27-alpine",
"network": "archy-net",
"network_aliases": [
"pine"
],
"pull_policy": "if-not-present"
},
"container_name": "pine",
"dependencies": [
{
"app_id": "pine-whisper"
},
{
"app_id": "pine-piper"
},
{
"app_id": "pine-openwakeword"
},
{
"storage": "128Mi"
}
],
"description": "A private voice assistant for your home. Pine runs speech-to-text (Whisper), text-to-speech (Piper) and wake-word detection (openWakeWord) on your own node and pairs with a PineVoice satellite speaker, so Home Assistant Assist works locally with nothing sent to the cloud. Ask it about your node — block height, sync, peers, Lightning balance — and, when a Claude API key is set, anything else.",
"environment": [],
"files": [
{
"content": "server {\n listen 80;\n server_name _;\n return 301 https://$host:10381$request_uri;\n}\nserver {\n listen 443 ssl;\n server_name _;\n ssl_certificate /etc/nginx/tls.crt;\n ssl_certificate_key /etc/nginx/tls.key;\n root /usr/share/nginx/html;\n index index.html;\n # Live node facts for the status card — proxied to the node's\n # public status tier so the (https) page can fetch same-origin.\n location = /node-status {\n proxy_pass http://host.containers.internal:80/api/pine/status;\n proxy_http_version 1.1;\n proxy_set_header Host $host;\n proxy_connect_timeout 5s;\n proxy_read_timeout 10s;\n }\n location / { try_files $uri $uri/ /index.html; }\n}\n",
"overwrite": true,
"path": "/var/lib/archipelago/pine/nginx.conf"
},
{
"content": "<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>Pine — connect your speaker</title>\n <style>\n :root { color-scheme: dark; }\n * { box-sizing: border-box; }\n body { margin: 0; font: 16px/1.6 system-ui, -apple-system, sans-serif;\n background: #0f1512; color: #e7efe9; }\n .wrap { max-width: 520px; margin: 0 auto; padding: 40px 22px 64px; }\n header { display: flex; align-items: center; gap: 14px; margin-bottom: 6px; }\n header svg { width: 52px; height: 52px; flex: 0 0 auto; }\n h1 { font-size: 26px; margin: 0; }\n .tag { color: #8fbfa5; font-size: 14px; margin: 2px 0 0; }\n .card { background: #16201b; border: 1px solid #24332b;\n border-radius: 14px; padding: 20px; margin: 20px 0; }\n .status .row { display: flex; gap: 10px; align-items: baseline; font-size: 14px; }\n .status .row b { min-width: 84px; color: #9fd3b6; }\n .status code { background: #0f1512; padding: 1px 6px; border-radius: 5px;\n font-size: 13px; color: #cfe9d9; }\n label { display: block; font-size: 13px; color: #9fbfad; margin: 14px 0 5px; }\n input { width: 100%; padding: 11px 12px; font-size: 15px; color: #e7efe9;\n background: #0f1512; border: 1px solid #2b3d33; border-radius: 9px;\n outline: none; }\n input:focus { border-color: #4fae82; }\n button { width: 100%; margin-top: 18px; padding: 13px; font-size: 15px;\n font-weight: 600; color: #06110b; background: #7fd6a6; border: 0;\n border-radius: 10px; cursor: pointer; transition: filter .15s; }\n button:hover:not(:disabled) { filter: brightness(1.08); }\n button:disabled { opacity: .45; cursor: default; }\n #log { margin-top: 16px; padding: 12px; min-height: 68px; font-size: 13px;\n font-family: ui-monospace, monospace; white-space: pre-wrap;\n background: #0b100d; border: 1px solid #1e2a23; border-radius: 9px;\n color: #a9c6b6; max-height: 220px; overflow-y: auto; }\n .ok { color: #7fd6a6; } .err { color: #ee8f8f; } .warn { color: #e8c878; }\n .ha { color: #6d8578; font-size: 13px; margin-top: 22px; }\n .ha b { color: #9fbfad; }\n .insecure { display: none; background: #2a1f12; border-color: #4a3418;\n color: #e8c878; font-size: 13px; }\n </style>\n</head>\n<body>\n <div class=\"wrap\">\n <header>\n <svg viewBox=\"0 0 512 512\" aria-hidden=\"true\"><g fill=\"#7fd6a6\">\n <rect x=\"236\" y=\"396\" width=\"40\" height=\"72\" rx=\"6\"/>\n <path d=\"M256 44 L336 168 L296 168 L256 108 L216 168 L176 168 Z\"/>\n <path d=\"M256 150 L360 300 L300 300 L256 236 L212 300 L152 300 Z\"/>\n <path d=\"M256 262 L392 430 L120 430 L256 262 Z\"/>\n </g></svg>\n <div><h1>Pine</h1>\n <p class=\"tag\">Connect your speaker — everything stays on your node.</p></div>\n </header>\n\n <div class=\"card status\">\n <div class=\"row\"><b>Whisper</b><span>speech-to-text ready on <code>:10300</code></span></div>\n <div class=\"row\"><b>Piper</b><span>text-to-speech ready on <code>:10200</code></span></div>\n <div class=\"row\"><b>Wake word</b><span>“Hey Jarvis” on the speaker (openWakeWord on <code>:10400</code>)</span></div>\n <div class=\"row\"><b>Speaker</b><span>put it in pairing mode — ring LED blinking yellow</span></div>\n </div>\n\n <div class=\"card status\">\n <div class=\"row\"><b>Node</b><span id=\"ns-node\">checking…</span></div>\n <div class=\"row\"><b>Bitcoin</b><span id=\"ns-btc\">—</span></div>\n <div class=\"row\"><b>Peers</b><span id=\"ns-peers\">—</span></div>\n </div>\n\n <div class=\"card insecure\" id=\"insecure\">\n This page isnt running over HTTPS, so the browser blocks Bluetooth.\n Open it via its <b>https://…:10380</b> address (accept the self-signed\n certificate) and the button below will work.\n </div>\n\n <div class=\"card\">\n <label for=\"ssid\">WiFi network (SSID)</label>\n <input id=\"ssid\" autocomplete=\"off\" spellcheck=\"false\" placeholder=\"Your 2.4 GHz network\">\n <label for=\"pass\">WiFi password — typed here, sent only over Bluetooth to the speaker</label>\n <input id=\"pass\" type=\"password\" autocomplete=\"off\">\n <button id=\"go\">Connect Pine to WiFi</button>\n <div id=\"log\">Ready. Click the button, then pick “PineVoice” in the Bluetooth popup.</div>\n </div>\n\n <p class=\"ha\">After WiFi joins, one manual step remains — pair the\n speaker in Home Assistant: <b>Settings → Devices &amp; services →\n Add Wyoming Protocol</b>, host = the speakers IP, port\n <b>10700</b>. Whisper, Piper, openWakeWord and the Assist pipeline\n are wired up automatically when Pine installs. Wake word:\n <b>“Hey Jarvis.”</b> Ask node things like <i>“whats the block\n height?”</i>, <i>“how many peers?”</i>, <i>“is the node\n synced?”</i> or <i>“whats my lightning balance?”</i> — and when a\n Claude API key is set on the node, anything else gets answered by\n Claude. New mesh messages are announced on the speaker too.</p>\n <p class=\"ha\">Troubleshooting: if it hears you (LED reacts) but answers\n are silent, unplug and replug the speaker — an interrupted answer can\n wedge its audio output until it reboots.</p>\n </div>\n\n <script>\n \"use strict\";\n // Improv-over-BLE (improv-wifi spec, verified vs improv-wifi-sdk 1.4.0).\n const SVC = \"00467768-6228-2272-4663-277478268000\";\n const CHAR_STATE = \"00467768-6228-2272-4663-277478268001\";\n const CHAR_ERROR = \"00467768-6228-2272-4663-277478268002\";\n const CHAR_RPC = \"00467768-6228-2272-4663-277478268003\";\n const CHAR_RESULT = \"00467768-6228-2272-4663-277478268004\";\n const STATES = {1:\"authorization required\",2:\"authorized\",3:\"provisioning…\",4:\"PROVISIONED\"};\n const ERRORS = {1:\"invalid RPC packet\",2:\"unknown RPC command\",\n 3:\"unable to connect — wrong password, or the SSID isn't reachable on 2.4 GHz\",\n 4:\"not authorized — press the button on the device\",5:\"bad hostname\",\n 255:\"unknown device error\"};\n\n const logEl = document.getElementById(\"log\");\n const log = (msg, cls) => { const l = document.createElement(\"div\");\n if (cls) l.className = cls; l.textContent = msg; logEl.appendChild(l);\n logEl.scrollTop = logEl.scrollHeight; };\n const hex = dv => [...new Uint8Array(dv.buffer, dv.byteOffset, dv.byteLength)]\n .map(b => b.toString(16).padStart(2, \"0\")).join(\" \");\n\n const btn = document.getElementById(\"go\");\n if (!navigator.bluetooth) {\n document.getElementById(\"insecure\").style.display = \"block\";\n logEl.textContent = \"Web Bluetooth unavailable — open this page over https (see note above).\";\n }\n\n btn.addEventListener(\"click\", async () => {\n const ssid = document.getElementById(\"ssid\").value.trim();\n const pass = document.getElementById(\"pass\").value;\n if (!ssid) { log(\"Enter your WiFi network name first.\", \"err\"); return; }\n if (!navigator.bluetooth) { log(\"No Web Bluetooth — open this page over https.\", \"err\"); return; }\n btn.disabled = true; logEl.textContent = \"\";\n let device;\n try {\n log(\"Opening Bluetooth device chooser…\");\n device = await navigator.bluetooth.requestDevice({\n filters: [{ services: [SVC] }, { namePrefix: \"PineVoice\" }],\n optionalServices: [SVC],\n });\n log(`Selected: ${device.name || \"(unnamed)\"}`);\n device.addEventListener(\"gattserverdisconnected\", () => log(\"BLE disconnected.\"));\n log(\"Connecting…\");\n const gatt = await device.gatt.connect();\n const svc = await gatt.getPrimaryService(SVC);\n const stateChar = await svc.getCharacteristic(CHAR_STATE);\n const errorChar = await svc.getCharacteristic(CHAR_ERROR);\n const rpcChar = await svc.getCharacteristic(CHAR_RPC);\n const resultChar = await svc.getCharacteristic(CHAR_RESULT);\n\n let done, fail;\n const outcome = new Promise((res, rej) => { done = res; fail = rej; });\n let authorize; const authorized = new Promise(res => { authorize = res; });\n let state = -1;\n const onState = (s, via = \"\") => {\n if (s === state) return; state = s;\n log(`Device state${via}: ${STATES[s] || s}`, s === 4 ? \"ok\" : undefined);\n if (s >= 2) authorize();\n if (s === 4) done(null);\n };\n stateChar.addEventListener(\"characteristicvaluechanged\",\n e => onState(e.target.value.getUint8(0)));\n errorChar.addEventListener(\"characteristicvaluechanged\", e => {\n const c = e.target.value.getUint8(0);\n if (c !== 0) fail(new Error(ERRORS[c] || `error ${c}`)); });\n resultChar.addEventListener(\"characteristicvaluechanged\", e => {\n const v = e.target.value; log(`RPC result: ${hex(v)}`);\n if (v.byteLength > 2 && v.getUint8(1) > 0) {\n const n = v.getUint8(2); const b = new Uint8Array(n);\n for (let i = 0; i < n; i++) b[i] = v.getUint8(3 + i);\n done(new TextDecoder().decode(b)); } });\n await stateChar.startNotifications();\n await errorChar.startNotifications();\n await resultChar.startNotifications();\n onState((await stateChar.readValue()).getUint8(0));\n\n const poller = setInterval(async () => {\n try { onState((await stateChar.readValue()).getUint8(0), \" (polled)\");\n const ec = (await errorChar.readValue()).getUint8(0);\n if (ec !== 0) fail(new Error(ERRORS[ec] || `error ${ec}`)); } catch {} }, 2000);\n\n let nextUrl;\n try {\n if (state === 1) {\n log(\"Authorization required — press the centre button on the speaker now.\", \"warn\");\n await Promise.race([authorized, outcome,\n new Promise((_, rej) => setTimeout(\n () => rej(new Error(\"timed out waiting for the button press\")), 60000))]);\n log(\"Authorized.\", \"ok\");\n }\n const enc = new TextEncoder();\n const sb = enc.encode(ssid), pb = enc.encode(pass);\n const data = new Uint8Array([sb.length, ...sb, pb.length, ...pb]);\n const pkt = new Uint8Array([1, data.length, ...data, 0]);\n pkt[pkt.length - 1] = pkt.reduce((s, b) => s + b, 0);\n log(`Sending WiFi credentials for “${ssid}”…`);\n if (rpcChar.writeValueWithResponse) await rpcChar.writeValueWithResponse(pkt);\n else await rpcChar.writeValue(pkt);\n log(\"Credentials delivered — speaker acknowledged.\", \"ok\");\n log(\"Joining WiFi… (the speaker plays a sound within ~20s either way)\");\n const timeout = new Promise((_, rej) => setTimeout(\n () => rej(new Error(\"timed out after 60s waiting for the device\")), 60000));\n nextUrl = await Promise.race([outcome, timeout]);\n } finally { clearInterval(poller); }\n\n log(\"✓ PROVISIONED — the ring LED should breathe dim magenta.\", \"ok\");\n if (nextUrl) log(`Device reports next step: ${nextUrl}`, \"ok\");\n try { gatt.disconnect(); } catch {}\n } catch (err) {\n log(`✗ ${err.name || \"Error\"}: ${err.message}`, \"err\");\n if (err.name === \"NotFoundError\")\n log(\"No speaker matched, or you closed the chooser. LED blinking yellow? \"\n + \"Hold the dot button 15s to reset.\", \"warn\");\n if (err.name === \"NetworkError\")\n log(\"BLE link dropped — move closer, and make sure the Mac isn't already \"\n + \"paired to the speaker (it can hold the link exclusively).\", \"warn\");\n try { device?.gatt?.disconnect(); } catch {}\n } finally { btn.disabled = false; }\n });\n\n // Live node status card — public tier of /api/pine/status via the\n // same-origin /node-status proxy. Best-effort: failures just show\n // \"unavailable\" and retry on the next tick.\n const nsNode = document.getElementById(\"ns-node\");\n const nsBtc = document.getElementById(\"ns-btc\");\n const nsPeers = document.getElementById(\"ns-peers\");\n async function refreshNodeStatus() {\n try {\n const r = await fetch(\"/node-status\", { cache: \"no-store\" });\n if (!r.ok) throw new Error(String(r.status));\n const s = await r.json();\n const up = Math.floor((s.uptime_seconds || 0) / 3600);\n nsNode.textContent = `Archipelago ${s.version || \"?\"} — up ${up}h`;\n if (s.bitcoin && s.bitcoin.height != null) {\n const pct = s.bitcoin.sync_percent;\n nsBtc.textContent = `block ${s.bitcoin.height}` +\n (pct != null ? (pct >= 99.99 ? \" — synced\" : ` — ${pct}% synced`) : \"\");\n } else {\n nsBtc.textContent = \"not running\";\n }\n const btcPeers = s.bitcoin && s.bitcoin.peers != null ? s.bitcoin.peers : \"?\";\n const meshPeers = s.mesh ? s.mesh.peers : 0;\n nsPeers.textContent = `${btcPeers} bitcoin` +\n (s.mesh && s.mesh.enabled ? `, ${meshPeers} mesh` : \"\");\n } catch {\n nsNode.textContent = \"node status unavailable\";\n nsBtc.textContent = \"—\";\n nsPeers.textContent = \"—\";\n }\n }\n refreshNodeStatus();\n setInterval(refreshNodeStatus, 30000);\n </script>\n</body>\n</html>\n",
"overwrite": true,
"path": "/var/lib/archipelago/pine/index.html"
}
],
"health_check": {
"endpoint": "localhost:443",
"interval": "30s",
"retries": 5,
"start_period": "10s",
"timeout": "5s",
"type": "tcp"
},
"id": "pine",
"interfaces": {
"main": {
"description": "Connect your speaker to WiFi and check the voice assistant",
"name": "Pine",
"path": "/",
"port": 10380,
"protocol": "http",
"type": "ui"
}
},
"metadata": {
"author": "Archipelago",
"category": "home",
"icon": "/assets/img/app-icons/pine.svg",
"launch": {
"open_in_new_tab": true
},
"license": "MIT",
"repo": "https://github.com/rhasspy/wyoming",
"tags": [
"home",
"voice",
"assistant",
"privacy"
],
"website": "https://github.com/rhasspy/wyoming"
},
"name": "Pine",
"ports": [
{
"container": 80,
"host": 10380,
"protocol": "tcp"
},
{
"container": 443,
"host": 10381,
"protocol": "tcp"
}
],
"resources": {
"memory_limit": "64Mi"
},
"security": {
"capabilities": [
"CHOWN",
"DAC_OVERRIDE",
"SETGID",
"SETUID",
"NET_BIND_SERVICE"
],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": false
},
"version": "1.3.0",
"volumes": [
{
"options": [
"ro"
],
"source": "/var/lib/archipelago/pine/nginx.conf",
"target": "/etc/nginx/conf.d/default.conf",
"type": "bind"
},
{
"options": [
"ro"
],
"source": "/var/lib/archipelago/pine/tls.crt",
"target": "/etc/nginx/tls.crt",
"type": "bind"
},
{
"options": [
"ro"
],
"source": "/var/lib/archipelago/pine/tls.key",
"target": "/etc/nginx/tls.key",
"type": "bind"
},
{
"options": [
"ro"
],
"source": "/var/lib/archipelago/pine/index.html",
"target": "/usr/share/nginx/html/index.html",
"type": "bind"
}
]
}
},
"version": "1.3.0"
},
"pine-openwakeword": {
"manifest": {
"app": {
"category": "home",
"container": {
"custom_args": [
"--preload-model",
"ok_nabu",
"--custom-model-dir",
"/custom"
],
"image": "docker.io/rhasspy/wyoming-openwakeword:2.1.0",
"network": "archy-net",
"network_aliases": [
"pine-openwakeword"
],
"pull_policy": "if-not-present"
},
"container_name": "pine-openwakeword",
"dependencies": [
{
"storage": "512Mi"
}
],
"description": "Wyoming-protocol openWakeWord wake-word engine. Internal Pine voice-assistant stack member — lets Assist pipelines run wake-word detection on the node (groundwork for the custom \"Yo Archy\" wake word; stock models like \"ok nabu\" ship with the image).",
"environment": [],
"health_check": {
"endpoint": "localhost:10400",
"interval": "30s",
"retries": 5,
"start_period": "30s",
"timeout": "5s",
"type": "tcp"
},
"id": "pine-openwakeword",
"metadata": {
"author": "Rhasspy / Home Assistant",
"icon": "/assets/img/app-icons/pine.svg",
"license": "MIT",
"repo": "https://github.com/rhasspy/wyoming-openwakeword",
"tags": [
"home",
"voice",
"wake-word",
"wyoming"
],
"website": "https://github.com/rhasspy/wyoming-openwakeword"
},
"name": "Pine Wake Word (openWakeWord)",
"ports": [
{
"container": 10400,
"host": 10400,
"protocol": "tcp"
}
],
"resources": {
"memory_limit": "512Mi"
},
"security": {
"capabilities": [],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": false
},
"version": "2.1.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/pine-openwakeword",
"target": "/custom",
"type": "bind"
}
]
}
},
"version": "2.1.0"
},
"pine-piper": {
"manifest": {
"app": {
"category": "home",
"container": {
"custom_args": [
"--voice",
"en_GB-alba-medium"
],
"image": "docker.io/rhasspy/wyoming-piper:2.2.2",
"network": "archy-net",
"network_aliases": [
"pine-piper"
],
"pull_policy": "if-not-present"
},
"container_name": "pine-piper",
"dependencies": [
{
"storage": "1Gi"
}
],
"description": "Wyoming-protocol Piper text-to-speech engine. Internal Pine voice-assistant stack member — gives Home Assistant Assist a natural voice for spoken responses on the PineVoice satellite.",
"environment": [],
"health_check": {
"endpoint": "localhost:10200",
"interval": "30s",
"retries": 5,
"start_period": "60s",
"timeout": "5s",
"type": "tcp"
},
"id": "pine-piper",
"metadata": {
"author": "Rhasspy / Home Assistant",
"icon": "/assets/img/app-icons/pine.svg",
"license": "MIT",
"repo": "https://github.com/rhasspy/wyoming-piper",
"tags": [
"home",
"voice",
"text-to-speech",
"wyoming"
],
"website": "https://github.com/rhasspy/wyoming-piper"
},
"name": "Pine Piper (TTS)",
"ports": [
{
"container": 10200,
"host": 10200,
"protocol": "tcp"
}
],
"resources": {
"memory_limit": "512Mi"
},
"security": {
"capabilities": [],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": false
},
"version": "2.2.2",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/pine-piper",
"target": "/data",
"type": "bind"
}
]
}
},
"version": "2.2.2"
},
"pine-whisper": {
"manifest": {
"app": {
"category": "home",
"container": {
"custom_args": [
"--model",
"base-int8",
"--language",
"en",
"--beam-size",
"1"
],
"image": "docker.io/rhasspy/wyoming-whisper:3.4.1",
"network": "archy-net",
"network_aliases": [
"pine-whisper"
],
"pull_policy": "if-not-present"
},
"container_name": "pine-whisper",
"dependencies": [
{
"storage": "2Gi"
}
],
"description": "Wyoming-protocol faster-whisper speech-to-text engine. Internal Pine voice-assistant stack member — turns speech captured by a PineVoice satellite into text for Home Assistant Assist.",
"environment": [],
"health_check": {
"endpoint": "localhost:10300",
"interval": "30s",
"retries": 5,
"start_period": "60s",
"timeout": "5s",
"type": "tcp"
},
"id": "pine-whisper",
"metadata": {
"author": "Rhasspy / Home Assistant",
"icon": "/assets/img/app-icons/pine.svg",
"license": "MIT",
"repo": "https://github.com/rhasspy/wyoming-faster-whisper",
"tags": [
"home",
"voice",
"speech-to-text",
"wyoming"
],
"website": "https://github.com/rhasspy/wyoming-faster-whisper"
},
"name": "Pine Whisper (STT)",
"ports": [
{
"container": 10300,
"host": 10300,
"protocol": "tcp"
}
],
"resources": {
"memory_limit": "2Gi"
},
"security": {
"capabilities": [],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": false
},
"version": "3.4.2",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/pine-whisper",
"target": "/data",
"type": "bind"
}
]
}
},
"version": "3.4.2"
},
"portainer": {
"image": "146.59.87.168:3000/lfg2025/portainer:2.19.4",
"manifest": {
"app": {
"category": "development",
"container": {
"data_uid": "1000:1000",
"image": "146.59.87.168:3000/lfg2025/portainer:2.19.4",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "1Gi"
}
],
"description": "Container management web UI for the local Podman socket.",
"environment": [],
"id": "portainer",
"interfaces": {
"main": {
"description": "Portainer web interface",
"name": "Web UI",
"path": "/",
"port": 9000,
"protocol": "http",
"type": "ui"
}
},
"metadata": {
"features": [
"Container management dashboard",
"Local Podman socket access",
"Compose stack storage"
],
"icon": "/assets/img/app-icons/portainer.webp",
"launch": {
"open_in_new_tab": true
},
"tier": "optional"
},
"name": "Portainer",
"ports": [
{
"container": 9000,
"host": 9000,
"protocol": "tcp"
}
],
"resources": {
"disk_limit": "1Gi",
"memory_limit": "256Mi"
},
"security": {
"capabilities": [
"CHOWN",
"SETUID",
"SETGID",
"DAC_OVERRIDE"
],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": false
},
"version": "2.19.4",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/portainer",
"target": "/data",
"type": "bind"
},
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/portainer/compose",
"target": "/data/compose",
"type": "bind"
},
{
"options": [
"rw"
],
"source": "/run/user/1000/podman/podman.sock",
"target": "/var/run/docker.sock",
"type": "bind"
}
]
}
},
"version": "2.19.4"
},
"router": {
"manifest": {
"app": {
"container": {
"image": "archipelago/router:1.0.0",
"image_signature": "cosign://...",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "500Mi"
}
],
"description": "Mesh routing and local network management. Provides device discovery, routing, and network topology visualization.",
"environment": [
"NETWORK_INTERFACE=eth0",
"MESH_ENABLED=true",
"DEVICE_DISCOVERY=true"
],
"health_check": {
"endpoint": "http://localhost:8084",
"interval": "30s",
"path": "/health",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "router",
"name": "Mesh Router",
"networking": {
"device_discovery": true,
"local_network_access": true,
"mesh_enabled": true,
"routing_protocols": [
"olsr",
"babel"
]
},
"ports": [
{
"container": 8080,
"host": 8084,
"protocol": "tcp"
},
{
"container": 5353,
"host": 5353,
"protocol": "udp"
},
{
"container": 1900,
"host": 1900,
"protocol": "udp"
}
],
"resources": {
"cpu_limit": 2,
"disk_limit": "500Mi",
"memory_limit": "512Mi"
},
"security": {
"apparmor_profile": "router",
"capabilities": [
"NET_ADMIN",
"NET_RAW"
],
"network_policy": "host",
"no_new_privileges": true,
"readonly_root": true,
"seccomp_profile": "default",
"user": 1000
},
"version": "1.0.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/router",
"target": "/app/data",
"type": "bind"
},
{
"options": [
"ro"
],
"source": "/var/run/dbus",
"target": "/var/run/dbus",
"type": "bind"
}
]
}
},
"version": "1.0.0"
},
"routstr": {
"image": "146.59.87.168:3000/lfg2025/routstr:v0.4.3",
"version": "v0.4.3"
},
"searxng": {
"image": "146.59.87.168:3000/lfg2025/searxng:latest",
"manifest": {
"app": {
"container": {
"image": "146.59.87.168:3000/lfg2025/searxng:latest",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "2Gi"
}
],
"description": "Privacy-respecting metasearch engine. Search the web without tracking.",
"environment": [
"SEARXNG_HOSTNAME=localhost",
"SEARXNG_BIND_ADDRESS=0.0.0.0:8080"
],
"health_check": {
"endpoint": "http://localhost:8080",
"interval": "30s",
"path": "/",
"retries": 5,
"timeout": "30s",
"type": "http"
},
"id": "searxng",
"name": "SearXNG",
"ports": [
{
"container": 8080,
"host": 8888,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 2,
"disk_limit": "2Gi",
"memory_limit": "1Gi"
},
"security": {
"apparmor_profile": "searxng",
"capabilities": [],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": true,
"seccomp_profile": "default",
"user": 1000
},
"version": "1.0.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/searxng",
"target": "/etc/searxng",
"type": "bind"
}
]
}
},
"version": "latest"
},
"strfry": {
"manifest": {
"app": {
"container": {
"image": "dockurr/strfry:1.0.4",
"image_signature": "cosign://...",
"pull_policy": "verify-signature"
},
"dependencies": [
{
"storage": "5Gi"
}
],
"description": "Lightweight Nostr relay written in C++. Alternative to nostr-rs-relay with lower resource usage.",
"files": [
{
"content": "##\n## Default strfry config\n##\n\n# Directory that contains the strfry LMDB database (restart required)\ndb = \"./strfry-db/\"\n\ndbParams {\n # Maximum number of threads/processes that can simultaneously have LMDB transactions open (restart required)\n maxreaders = 256\n\n # Size of mmap() to use when loading LMDB (default is 10TB, does *not* correspond to disk-space used) (restart required)\n mapsize = 10995116277760\n\n # Disables read-ahead when accessing the LMDB mapping. Reduces IO activity when DB size is larger than RAM. (restart required)\n noReadAhead = false\n}\n\nevents {\n # Maximum size of normalised JSON, in bytes\n maxEventSize = 65536\n\n # Events newer than this will be rejected\n rejectEventsNewerThanSeconds = 900\n\n # Events older than this will be rejected\n rejectEventsOlderThanSeconds = 94608000\n\n # Ephemeral events older than this will be rejected\n rejectEphemeralEventsOlderThanSeconds = 60\n\n # Ephemeral events will be deleted from the DB when older than this\n ephemeralEventsLifetimeSeconds = 300\n\n # Maximum number of tags allowed\n maxNumTags = 2000\n\n # Maximum size for tag values, in bytes\n maxTagValSize = 1024\n}\n\nrelay {\n # Interface to listen on. Use 0.0.0.0 to listen on all interfaces (restart required)\n bind = \"0.0.0.0\"\n\n # Port to open for the nostr websocket protocol (restart required)\n port = 7777\n\n # Set OS-limit on maximum number of open files/sockets (if 0, don't attempt to set) (restart required)\n nofiles = 0\n\n # HTTP header that contains the client's real IP, before reverse proxying (ie x-real-ip) (MUST be all lower-case)\n realIpHeader = \"\"\n\n info {\n # NIP-11: Name of this server. Short/descriptive (< 30 characters)\n name = \"Archipelago Strfry Relay\"\n\n # NIP-11: Detailed information about relay, free-form\n description = \"Self-hosted strfry Nostr relay on Archipelago.\"\n\n # NIP-11: Administrative nostr pubkey, for contact purposes\n pubkey = \"\"\n\n # NIP-11: Alternative administrative contact (email, website, etc)\n contact = \"\"\n\n # NIP-11: URL pointing to an image to be used as an icon for the relay\n icon = \"\"\n\n # List of supported lists as JSON array, or empty string to use default. Example: \"[1,2]\"\n nips = \"\"\n }\n\n # Maximum accepted incoming websocket frame size (should be larger than max event) (restart required)\n maxWebsocketPayloadSize = 131072\n\n # Maximum number of filters allowed in a REQ\n maxReqFilterSize = 200\n\n # Websocket-level PING message frequency (should be less than any reverse proxy idle timeouts) (restart required)\n autoPingSeconds = 55\n\n # If TCP keep-alive should be enabled (detect dropped connections to upstream reverse proxy)\n enableTcpKeepalive = false\n\n # How much uninterrupted CPU time a REQ query should get during its DB scan\n queryTimesliceBudgetMicroseconds = 10000\n\n # Maximum records that can be returned per filter\n maxFilterLimit = 500\n\n # Maximum number of subscriptions (concurrent REQs) a connection can have open at any time\n maxSubsPerConnection = 20\n\n writePolicy {\n # If non-empty, path to an executable script that implements the writePolicy plugin logic\n plugin = \"/app/write-policy.py\"\n }\n\n compression {\n # Use permessage-deflate compression if supported by client. Reduces bandwidth, but slight increase in CPU (restart required)\n enabled = true\n\n # Maintain a sliding window buffer for each connection. Improves compression, but uses more memory (restart required)\n slidingWindow = true\n }\n\n logging {\n # Dump all incoming messages\n dumpInAll = false\n\n # Dump all incoming EVENT messages\n dumpInEvents = false\n\n # Dump all incoming REQ/CLOSE messages\n dumpInReqs = false\n\n # Log performance metrics for initial REQ database scans\n dbScanPerf = false\n\n # Log reason for invalid event rejection? Can be disabled to silence excessive logging\n invalidEvents = true\n }\n\n numThreads {\n # Ingester threads: route incoming requests, validate events/sigs (restart required)\n ingester = 3\n\n # reqWorker threads: Handle initial DB scan for events (restart required)\n reqWorker = 3\n\n # reqMonitor threads: Handle filtering of new events (restart required)\n reqMonitor = 3\n\n # negentropy threads: Handle negentropy protocol messages (restart required)\n negentropy = 2\n }\n\n negentropy {\n # Support negentropy protocol messages\n enabled = true\n\n # Maximum records that sync will process before returning an error\n maxSyncEvents = 1000000\n }\n}\n",
"overwrite": true,
"path": "/var/lib/archipelago/strfry-config/strfry.conf"
}
],
"health_check": {
"endpoint": "http://127.0.0.1:7777",
"interval": "30s",
"path": "/health",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "strfry",
"name": "Strfry Nostr Relay",
"nostr_integration": {
"monetization_enabled": true,
"relay_type": "public"
},
"ports": [
{
"container": 7777,
"host": 8090,
"protocol": "tcp"
}
],
"resources": {
"cpu_limit": 1,
"disk_limit": "5Gi",
"memory_limit": "512Mi"
},
"security": {
"apparmor_profile": "nostr-relay",
"capabilities": [],
"network_policy": "isolated",
"no_new_privileges": true,
"readonly_root": true,
"seccomp_profile": "default"
},
"version": "0.9.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/strfry",
"target": "/app/strfry-db",
"type": "bind"
},
{
"options": [
"ro"
],
"source": "/var/lib/archipelago/strfry-config/strfry.conf",
"target": "/etc/strfry.conf",
"type": "bind"
}
]
}
},
"version": "0.9.0"
},
"tailscale": {
"image": "146.59.87.168:3000/lfg2025/tailscale:stable",
"version": "stable"
},
"uptime-kuma": {
"image": "146.59.87.168:3000/lfg2025/uptime-kuma:1",
"manifest": {
"app": {
"container": {
"custom_args": [
"--",
"node",
"server/server.js"
],
"image": "146.59.87.168:3000/lfg2025/uptime-kuma:1",
"network": "pasta",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "1Gi"
}
],
"description": "Self-hosted uptime monitoring.",
"environment": [
"TZ=UTC"
],
"health_check": {
"endpoint": "localhost:3001",
"interval": "30s",
"path": "/",
"retries": 3,
"timeout": "5s",
"type": "http"
},
"id": "uptime-kuma",
"metadata": {
"author": "Uptime Kuma",
"category": "data",
"icon": "/assets/img/app-icons/uptime-kuma.webp",
"launch": {
"open_in_new_tab": true
},
"repo": "https://github.com/louislam/uptime-kuma",
"tier": "recommended"
},
"name": "Uptime Kuma",
"ports": [
{
"container": 3001,
"host": 3002,
"protocol": "tcp"
}
],
"resources": {
"disk_limit": "1Gi",
"memory_limit": "256Mi"
},
"security": {
"capabilities": [
"CHOWN",
"FOWNER",
"SETUID",
"SETGID"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "1.23.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/uptime-kuma",
"target": "/app/data",
"type": "bind"
}
]
}
},
"version": "1"
},
"vaultwarden": {
"image": "146.59.87.168:3000/lfg2025/vaultwarden:1.30.0-alpine",
"manifest": {
"app": {
"container": {
"image": "146.59.87.168:3000/lfg2025/vaultwarden:1.30.0-alpine",
"network": "pasta",
"pull_policy": "if-not-present"
},
"dependencies": [
{
"storage": "1Gi"
}
],
"description": "Self-hosted password vault with zero-knowledge encryption.",
"environment": [],
"health_check": {
"endpoint": "localhost:80",
"interval": "30s",
"retries": 3,
"timeout": "5s",
"type": "tcp"
},
"id": "vaultwarden",
"interfaces": {
"main": {
"description": "Vaultwarden web vault",
"name": "Web UI",
"path": "/",
"port": 8082,
"protocol": "http",
"type": "ui"
}
},
"metadata": {
"author": "Vaultwarden",
"category": "data",
"icon": "/assets/img/app-icons/vaultwarden.webp",
"launch": {
"open_in_new_tab": true
},
"repo": "https://github.com/dani-garcia/vaultwarden",
"tier": "recommended"
},
"name": "Vaultwarden",
"ports": [
{
"container": 80,
"host": 8082,
"protocol": "tcp"
}
],
"resources": {
"disk_limit": "1Gi",
"memory_limit": "256Mi"
},
"security": {
"capabilities": [
"CHOWN",
"SETUID",
"SETGID",
"NET_BIND_SERVICE"
],
"network_policy": "isolated",
"readonly_root": false
},
"version": "1.30.0",
"volumes": [
{
"options": [
"rw"
],
"source": "/var/lib/archipelago/vaultwarden",
"target": "/data",
"type": "bind"
}
]
}
},
"version": "1.30.0-alpine"
}
},
"schema": 1,
"signature": "4054b2a8659b75a3810ed153798c0e8a08db49ce6237b8ae985e87ace8a35163c24a0190074abd2a8296309ebe9dd583f153642d46d4d7f0a6a4db255743380b",
"signed_by": "did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur",
"updated": "2026-07-23"
}