feat: one-command crew coordinator installer
This commit is contained in:
parent
ae1d7275e6
commit
f7316060ae
5 changed files with 123 additions and 4 deletions
14
AGENTS.md
14
AGENTS.md
|
|
@ -1418,7 +1418,9 @@ session with **no other memory of this project**. So:
|
||||||
it in when known.
|
it in when known.
|
||||||
**Profile discovery (built 2026-09-29):** the carrier is the existing kind-0
|
**Profile discovery (built 2026-09-29):** the carrier is the existing kind-0
|
||||||
**`cordn`** field (app convention, documented in NIP.md — not a new kind).
|
**`cordn`** field (app convention, documented in NIP.md — not a new kind).
|
||||||
**Crew-owned coordinator kit (2026-10-31):** `deploy/cordn/` is the
|
**Crew-owned coordinator kit (2026-10-31, install helper 2026-11-01):**
|
||||||
|
`deploy/cordn/install.sh` is the one-command installer (generates `.env`,
|
||||||
|
starts SQLite, prints nprofile/add link). `deploy/cordn/` remains the
|
||||||
production hand-off for crews: Docker Compose, SQLite volume, `.env` template,
|
production hand-off for crews: Docker Compose, SQLite volume, `.env` template,
|
||||||
backup/restore scripts and a runbook. Cordn connects **outbound** to ContextVM
|
backup/restore scripts and a runbook. Cordn connects **outbound** to ContextVM
|
||||||
relays; no inbound port, domain or TLS terminator is required for the
|
relays; no inbound port, domain or TLS terminator is required for the
|
||||||
|
|
@ -3489,6 +3491,16 @@ These components follow a consistent pattern using React's `forwardRef` and use
|
||||||
|
|
||||||
The project includes a **`skills/`** directory containing specialized implementation guides for specific features. Each skill has a comprehensive `SKILL.md` file with complete patterns, code examples, and best practices. **Always reference the appropriate SKILL.md file when implementing related functionality.**
|
The project includes a **`skills/`** directory containing specialized implementation guides for specific features. Each skill has a comprehensive `SKILL.md` file with complete patterns, code examples, and best practices. **Always reference the appropriate SKILL.md file when implementing related functionality.**
|
||||||
|
|
||||||
|
### Deferred: NIP-05 as a wallet address
|
||||||
|
**Do not set `lud16` to `name@skate-spots.org` until an LNURL-pay service exists.**
|
||||||
|
NIP-05 is a Nostr identity record; a Lightning address is a separate
|
||||||
|
`/.well-known/lnurlp/<name>` service. A Minibits-style wallet address needs the
|
||||||
|
NIP-05 server **and** an LNURL/mint/claim service (plus rate limits, backups,
|
||||||
|
privacy copy and a clear mint-trust model). Optional future paths:
|
||||||
|
self-hosted LNURL + Cashu mint, or a deliberate npub.cash/Minibits-style
|
||||||
|
integration. Until then keep NIP-05 as identity and use NWC/built-in Cashu;
|
||||||
|
never overwrite a user's custom `lud16`.
|
||||||
|
|
||||||
### Wallet Skills
|
### Wallet Skills
|
||||||
|
|
||||||
- **`skills/bitcoin-wallet/SKILL.md`**: Use when implementing Bitcoin wallet features - provides complete architecture for on-chain Bitcoin transactions, UTXO management, address derivation from Nostr keys, and transaction handling with proper security patterns. Read when building Bitcoin wallet functionality, adding on-chain transaction support, implementing UTXO management, or integrating Bitcoin with Nostr-based applications.
|
- **`skills/bitcoin-wallet/SKILL.md`**: Use when implementing Bitcoin wallet features - provides complete architecture for on-chain Bitcoin transactions, UTXO management, address derivation from Nostr keys, and transaction handling with proper security patterns. Read when building Bitcoin wallet functionality, adding on-chain transaction support, implementing UTXO management, or integrating Bitcoin with Nostr-based applications.
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,21 @@ coordinator in the APK.
|
||||||
|
|
||||||
## 1. Install
|
## 1. Install
|
||||||
|
|
||||||
|
### Easiest: one command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://git.skate-spots.org/skatespots/SkateSpots/raw/branch/main/deploy/cordn/install.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
It checks Docker, generates the coordinator key, writes a private `.env`, starts
|
||||||
|
SQLite persistence, waits for the coordinator to announce, and prints the
|
||||||
|
`nprofile`/add link.
|
||||||
|
|
||||||
|
No inbound port, domain or TLS setup is required. The coordinator connects
|
||||||
|
outbound to the ContextVM relays in `CORDN_RELAY_URLS`.
|
||||||
|
|
||||||
|
### Manual: from this directory
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
chmod 600 .env
|
chmod 600 .env
|
||||||
|
|
@ -40,6 +55,17 @@ If your build does not print an add link, derive the public key from the private
|
||||||
key using the Cordn CLI or `nostr-tools`, then encode an `nprofile` with the same
|
key using the Cordn CLI or `nostr-tools`, then encode an `nprofile` with the same
|
||||||
relay hints. Keep the private key on the server.
|
relay hints. Keep the private key on the server.
|
||||||
|
|
||||||
|
### Can I run it on a phone?
|
||||||
|
|
||||||
|
Not with this Docker kit. A phone can **administer** a coordinator, and Cordn's
|
||||||
|
own phone app is the hand-off path, but an always-on coordinator needs a machine
|
||||||
|
that stays awake and reachable. Android may suspend a foreground coordinator;
|
||||||
|
do not promise crew members that a phone is a reliable coordinator host.
|
||||||
|
|
||||||
|
For a crew that wants ownership without a VPS, an old Raspberry Pi or a
|
||||||
|
small always-on home server is the simplest reliable option. The coordinator
|
||||||
|
still needs no inbound port.
|
||||||
|
|
||||||
## 2. Adopt it in SkateSpots
|
## 2. Adopt it in SkateSpots
|
||||||
|
|
||||||
Either:
|
Either:
|
||||||
|
|
|
||||||
77
deploy/cordn/install.sh
Executable file
77
deploy/cordn/install.sh
Executable file
|
|
@ -0,0 +1,77 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# One-command crew coordinator installer.
|
||||||
|
#
|
||||||
|
# curl -fsSL https://git.skate-spots.org/skatespots/SkateSpots/raw/branch/main/deploy/cordn/install.sh | bash
|
||||||
|
#
|
||||||
|
# Creates a persistent Cordn coordinator with SQLite storage, starts it, and
|
||||||
|
# prints the nprofile/add link when the server announces itself. The coordinator
|
||||||
|
# connects OUT to ContextVM relays; it needs no inbound port or TLS domain.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")" 2>/dev/null || true
|
||||||
|
umask 077
|
||||||
|
|
||||||
|
need() { command -v "$1" >/dev/null || { echo "missing: $1" >&2; exit 1; }; }
|
||||||
|
need docker
|
||||||
|
need openssl
|
||||||
|
docker compose version >/dev/null 2>&1 || { echo "Docker Compose v2 is required (docker compose)." >&2; exit 1; }
|
||||||
|
|
||||||
|
if [ ! -f docker-compose.yml ]; then
|
||||||
|
cat > docker-compose.yml <<'YAML'
|
||||||
|
name: cordn
|
||||||
|
services:
|
||||||
|
cordn:
|
||||||
|
image: ghcr.io/cordn-msg/cordn:latest
|
||||||
|
container_name: cordn
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
CORDN_STORAGE_BACKEND: sqlite
|
||||||
|
CORDN_SQLITE_PATH: /data/cordn.sqlite
|
||||||
|
volumes:
|
||||||
|
- cordn-data:/data
|
||||||
|
volumes:
|
||||||
|
cordn-data:
|
||||||
|
YAML
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
NAME="${CORDN_SERVER_NAME:-skatespots-crew}"
|
||||||
|
RELAYS="${CORDN_RELAY_URLS:-wss://relay.contextvm.org,wss://relay2.contextvm.org}"
|
||||||
|
KEY="$(openssl rand -hex 32)"
|
||||||
|
cat > .env <<EOF
|
||||||
|
CORDN_SERVER_PRIVATE_KEY=$KEY
|
||||||
|
CORDN_RELAY_URLS=$RELAYS
|
||||||
|
CORDN_ANNOUNCED=true
|
||||||
|
CORDN_SERVER_NAME=$NAME
|
||||||
|
CORDN_STORAGE_BACKEND=sqlite
|
||||||
|
CORDN_SQLITE_PATH=/data/cordn.sqlite
|
||||||
|
CORDN_RATE_LIMIT_ENABLED=true
|
||||||
|
CORDN_RATE_LIMIT_REFILL_PER_MINUTE=250
|
||||||
|
CORDN_RATE_LIMIT_BURST=80
|
||||||
|
CORDN_MAX_KEY_PACKAGES_PER_IDENTITY=50
|
||||||
|
CORDN_MAX_LAST_RESORT_KEY_PACKAGES_PER_IDENTITY=1
|
||||||
|
EOF
|
||||||
|
chmod 600 .env
|
||||||
|
echo "Generated .env (keep it private; it contains the coordinator key)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker compose up -d
|
||||||
|
echo "Waiting for the coordinator to announce itself…"
|
||||||
|
|
||||||
|
for _ in $(seq 1 30); do
|
||||||
|
logs="$(docker compose logs --no-color 2>&1 || true)"
|
||||||
|
if printf '%s\n' "$logs" | grep -q 'nprofile'; then
|
||||||
|
printf '\n%s\n' "$logs" | grep -A12 -B2 'CORDN COORDINATOR'
|
||||||
|
echo
|
||||||
|
echo "Back up .env and the cordn-data volume before inviting the crew."
|
||||||
|
echo "Adopt it in SkateSpots: Settings → Coordinator, or Crew edit → Coordinator."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "The container started but no announcement appeared within 60s." >&2
|
||||||
|
echo "Inspect: docker compose logs --no-color" >&2
|
||||||
|
exit 1
|
||||||
|
|
@ -20,7 +20,7 @@ import { useTranslation } from '@/contexts/language';
|
||||||
import { toast } from '@/hooks/useToast';
|
import { toast } from '@/hooks/useToast';
|
||||||
import {
|
import {
|
||||||
CORDN_APP_URL,
|
CORDN_APP_URL,
|
||||||
CORDN_DOCKER_COMMAND,
|
CORDN_INSTALL_COMMAND,
|
||||||
CORDN_DEPLOY_KIT_URL,
|
CORDN_DEPLOY_KIT_URL,
|
||||||
CORDN_RELAY_URL,
|
CORDN_RELAY_URL,
|
||||||
coordinatorAddUrl,
|
coordinatorAddUrl,
|
||||||
|
|
@ -315,11 +315,11 @@ export function CoordinatorSettingsCard() {
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-2 rounded-xl bg-black/40 border border-white/5 px-3 py-2">
|
<div className="flex items-center gap-2 rounded-xl bg-black/40 border border-white/5 px-3 py-2">
|
||||||
<code className="flex-1 min-w-0 truncate text-[11px] font-mono text-gray-300">
|
<code className="flex-1 min-w-0 truncate text-[11px] font-mono text-gray-300">
|
||||||
{CORDN_DOCKER_COMMAND}
|
{CORDN_INSTALL_COMMAND}
|
||||||
</code>
|
</code>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => void copyText(CORDN_DOCKER_COMMAND, t('socialUi.coordinator.commandCopied'), t('socialUi.coordinator.copyFailed'), t('socialUi.coordinator.copyManually'))}
|
onClick={() => void copyText(CORDN_INSTALL_COMMAND, t('socialUi.coordinator.commandCopied'), t('socialUi.coordinator.copyFailed'), t('socialUi.coordinator.copyManually'))}
|
||||||
className="shrink-0 text-muted-foreground hover:text-white transition-colors"
|
className="shrink-0 text-muted-foreground hover:text-white transition-colors"
|
||||||
aria-label={t('socialUi.coordinator.copyCommand')}
|
aria-label={t('socialUi.coordinator.copyCommand')}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,10 @@ export const CORDN_HOME = 'https://cordn.net';
|
||||||
export const CORDN_DOCKER_COMMAND =
|
export const CORDN_DOCKER_COMMAND =
|
||||||
'docker run -d --name cordn --restart unless-stopped -v cordn-data:/data -e CORDN_STORAGE_BACKEND=sqlite -e CORDN_SQLITE_PATH=/data/cordn.sqlite -e CORDN_RELAY_URLS=wss://relay.contextvm.org -e CORDN_ANNOUNCED=true -e CORDN_SERVER_PRIVATE_KEY=YOUR_64_HEX_KEY ghcr.io/cordn-msg/cordn:latest';
|
'docker run -d --name cordn --restart unless-stopped -v cordn-data:/data -e CORDN_STORAGE_BACKEND=sqlite -e CORDN_SQLITE_PATH=/data/cordn.sqlite -e CORDN_RELAY_URLS=wss://relay.contextvm.org -e CORDN_ANNOUNCED=true -e CORDN_SERVER_PRIVATE_KEY=YOUR_64_HEX_KEY ghcr.io/cordn-msg/cordn:latest';
|
||||||
|
|
||||||
|
/** Self-contained server installer: creates .env, starts SQLite, prints nprofile. */
|
||||||
|
export const CORDN_INSTALL_COMMAND =
|
||||||
|
'curl -fsSL https://git.skate-spots.org/skatespots/SkateSpots/raw/branch/main/deploy/cordn/install.sh | bash';
|
||||||
|
|
||||||
/** Production deployment kit (persistent SQLite volume + backup/restore). */
|
/** Production deployment kit (persistent SQLite volume + backup/restore). */
|
||||||
export const CORDN_DEPLOY_KIT_URL =
|
export const CORDN_DEPLOY_KIT_URL =
|
||||||
'https://git.skate-spots.org/skatespots/SkateSpots/tree/main/deploy/cordn';
|
'https://git.skate-spots.org/skatespots/SkateSpots/tree/main/deploy/cordn';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue