diff --git a/.gitmodules b/.gitmodules index eae6c18..26b4b02 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,7 @@ path = vss-server url = git@github.com:synonymdev/vss-server.git branch = chore/rebase-stag-on-main +[submodule "homegate"] + path = homegate + url = git@github.com:pubky/homegate.git + branch = master diff --git a/CHANGELOG.md b/CHANGELOG.md index 700aef8..2876e50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Preserve LNURL-pay invoice millisatoshi precision by creating invoices with LND `value_msat` instead of truncating callback amounts to sats ### Added -- Trezor User Env Docker service and `scripts/trezor-emulator` helper for quickly smoke-testing Bitkit app Trezor PRs +- Homegate Docker Compose service with dedicated PostgreSQL storage, local homeserver admin mock, and README setup flow +- Repo-managed Trezor User Env Docker service and `scripts/trezor-emulator` helper for quickly smoke-testing Bitkit app Trezor PRs - Support `amount_msat` query param in `/generate/bolt11` endpoint for sub-sat precision invoices - `bolt11` command in `bitcoin-cli` for creating regular Lightning invoices (supports `--msat` and `-m` memo) - LND hold invoice commands in `bitcoin-cli`: `holdinvoice`, `settleinvoice`, `cancelinvoice` diff --git a/README.md b/README.md index 6697b7c..8bb2e88 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ A complete Docker-based development environment for Bitcoin and Lightning Networ - **LNURL Server**: Lightning payment server with LNURL support - **LDK Backup Server**: Lightning Development Kit backup service - **VSS Server**: Versioned Storage Server for app and ldk-node state backups +- **Homegate**: Pubky Homeserver signup gatekeeper with local admin API mock ## Quick Start @@ -27,6 +28,7 @@ A complete Docker-based development environment for Bitcoin and Lightning Networ ```bash curl http://localhost:3000/health + curl http://localhost:6288/ ``` ## Services Overview @@ -71,6 +73,16 @@ A complete Docker-based development environment for Bitcoin and Lightning Networ - **Port**: 5050 - **Features**: RS256 JWT authentication +### Homegate + +- **Port**: 6288 +- **Database**: Dedicated `homegate-postgres` service, exposed on host port 5433 by default +- **Admin mock**: `homegate-admin-mock` on port 6289 +- **Features**: + - Pubky Homeserver signup-code gatekeeping + - IP verification enabled by default for local testing + - SMS and Lightning verification disabled by default unless provider-backed config is added + ### LNURL-Auth Server - **Port**: 5005 @@ -104,10 +116,39 @@ curl -s http://localhost:3000/.well-known/lnurlp/alice | jq # VSS Health Check curl -v http://localhost:5050/vss/getObject + +# Homegate service check +curl http://localhost:6288/ ``` ## Development +### Homegate + +Homegate starts with the default `docker compose up -d` stack. Its source is included as the `homegate` submodule, and the container reads [homegate-config.toml](homegate-config.toml), which points at a local `homegate-admin-mock` service so startup does not require real Pubky Homeserver credentials. + +Useful commands: + +```bash +# Rebuild and start Homegate with its database and admin mock +docker compose up --build -d homegate + +# Check the service root +curl http://localhost:6288/ + +# Exercise IP verification against the local admin mock +curl -X POST http://localhost:6288/ip_verification + +# Follow logs +docker compose logs -f homegate +``` + +To test against a real Homeserver admin API or provider-backed SMS/Lightning verification, update [homegate-config.toml](homegate-config.toml) before starting the service: + +- Point `[homeserver].admin_api_url` and `admin_password` at the real admin API +- Add `[sms_verification]` with Prelude credentials for SMS verification +- Add `[ln_verification]` with PhoenixD credentials for Lightning verification + ### bitcoin-cli helper The `bitcoin-cli` script provides shortcuts for common operations. Run `./bitcoin-cli --help` for full usage. @@ -159,6 +200,7 @@ docker compose logs -f # Specific service docker compose logs -f lnurl-server docker compose logs -f vss-server +docker compose logs -f homegate docker compose logs -f lnd docker compose logs -f bitcoind ``` @@ -173,7 +215,7 @@ Use this section as the entry point when checking Bitkit app PRs or merged featu ./scripts/trezor-emulator start ``` -The macOS Trezor User Env service is included in the default `docker compose up -d` stack. The helper starts or reuses that service, then resets Bridge and the emulator into the deterministic review state. Linux users can start the host-network service with `docker compose --profile trezor-linux up -d trezor-user-env-linux`. +The macOS Trezor User Env service is included in the default `docker compose up -d` stack. The helper uses this repo-managed Compose service, then resets Bridge and the emulator into the deterministic review state. Linux users can start the host-network service with `docker compose --profile trezor-linux up -d trezor-user-env-linux`. The helper starts the official Trezor User Env without its regtest stack, launches Bridge, wipes a deterministic T2T1 emulator, and sets it up with the `all all ...` seed and `Bitkit Test Trezor` label. It uses `scripts/trezor-controller.py` inside the container to talk to the User Env websocket controller. @@ -319,6 +361,11 @@ Key environment variables in `docker-compose.yml`: - `BITCOIN_RPC_PORT`: Bitcoin RPC port (default: `43782`) - `LND_REST_HOST`: LND REST API host (default: `lnd`) - `LND_REST_PORT`: LND REST API port (default: `8080`) +- `HOMEGATE_PORT`: Host port for Homegate (default: `6288`) +- `HOMEGATE_POSTGRES_PORT`: Host port for Homegate PostgreSQL (default: `5433`) +- `HOMEGATE_ADMIN_MOCK_PORT`: Host port for the local Homegate admin API mock (default: `6289`) +- `HOMEGATE_ADMIN_MOCK_PASSWORD`: Admin password expected by the local Homegate admin API mock (default: `admin`) +- `HOMEGATE_ADMIN_MOCK_PUBKY`: Homeserver public key returned by the local Homegate admin API mock ### Volumes @@ -327,6 +374,8 @@ Key environment variables in `docker-compose.yml`: - `./lnurl-server/keys:/app/keys:ro` - RSA keys for JWT signing - `bitcoin_home` - Bitcoin blockchain data - `postgres_data` - VSS PostgreSQL database +- `homegate_postgres_data` - Homegate PostgreSQL database +- `homegate_data` - Homegate local state, including the generated phone-number pepper ### VSS Server Setup @@ -362,7 +411,7 @@ rm -rf ./data ./test-data # rm -rf lnurl-server/keys/ private.pem public.pem # Then Generate new RSA keys (see above) -# Initialize vss-server submodule: +# Initialize submodules: git submodule update --init --recursive # Start services @@ -383,6 +432,12 @@ docker compose up --build -d 2. Check macaroon files exist 3. Verify network connectivity between containers +### Homegate exits immediately + +1. Check logs: `docker compose logs homegate homegate-admin-mock` +2. Verify [homegate-config.toml](homegate-config.toml) is mounted and points `[homeserver].admin_api_url` at a reachable admin API +3. Confirm the configured homeserver admin API responds to `/info`; Homegate stops during startup if that check fails + ### Bitcoin RPC issues 1. Ensure Bitcoin Core is fully synced diff --git a/docker-compose.yml b/docker-compose.yml index 86db55e..136ead0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -173,6 +173,45 @@ services: timeout: 5s retries: 5 + homegate-postgres: + container_name: homegate-postgres + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_DB: pubky_homegate + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + volumes: + - homegate_postgres_data:/var/lib/postgresql/data + ports: + - "${HOMEGATE_POSTGRES_PORT:-5433}:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d pubky_homegate"] + interval: 10s + timeout: 5s + retries: 5 + + homegate-admin-mock: + container_name: homegate-admin-mock + image: node:22-alpine + restart: unless-stopped + working_dir: /app + command: ["node", "server.js"] + environment: + HOMEGATE_ADMIN_MOCK_HOST: 0.0.0.0 + HOMEGATE_ADMIN_MOCK_PORT: 6289 + HOMEGATE_ADMIN_MOCK_PASSWORD: ${HOMEGATE_ADMIN_MOCK_PASSWORD:-admin} + HOMEGATE_ADMIN_MOCK_PUBKY: ${HOMEGATE_ADMIN_MOCK_PUBKY:-ufibwbmed6jeq9k4p583go95wofakh9fwpp4k734trq79pd9u1uy} + volumes: + - ./homegate-admin-mock:/app:ro + ports: + - "${HOMEGATE_ADMIN_MOCK_PORT:-6289}:6289" + healthcheck: + test: ["CMD", "node", "-e", "fetch('http://localhost:6289/info', { headers: { 'X-Admin-Password': process.env.HOMEGATE_ADMIN_MOCK_PASSWORD || 'admin' } }).then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"] + interval: 10s + timeout: 5s + retries: 5 + lnurl-auth-server: container_name: lnurl-auth-server image: lnurl-auth-server:latest @@ -239,6 +278,24 @@ services: ports: - "5050:5050" + homegate: + container_name: homegate + image: homegate:latest + build: + context: ./homegate + dockerfile: Dockerfile + restart: unless-stopped + depends_on: + homegate-postgres: + condition: service_healthy + homegate-admin-mock: + condition: service_healthy + volumes: + - homegate_data:/root/.homegate + - ./homegate-config.toml:/root/.homegate/config.toml:ro + ports: + - "${HOMEGATE_PORT:-6288}:6288" + trezor-user-env-mac: image: ghcr.io/trezor/trezor-user-env ports: @@ -259,7 +316,6 @@ services: - ./.trezor-user-env/firmware/user_downloaded:/trezor-user-env/src/binaries/firmware/bin/user_downloaded trezor-user-env-linux: - container_name: trezor-user-env.unix image: ghcr.io/trezor/trezor-user-env profiles: - trezor-linux @@ -276,5 +332,7 @@ volumes: bitcoin_home: postgres_data: lnurl_auth_data: + homegate_postgres_data: + homegate_data: networks: {} diff --git a/docs/trezor-emulator.md b/docs/trezor-emulator.md index c18ce10..47f4dfa 100644 --- a/docs/trezor-emulator.md +++ b/docs/trezor-emulator.md @@ -10,14 +10,12 @@ Bitkit app PRs that need Trezor hardware behavior can use the official Trezor Us On macOS, the Trezor User Env service is part of the default compose stack, so `docker compose up -d` starts it with the rest of the Bitkit services. The helper is still useful because it resets Bridge and the emulator into the deterministic review state. -Linux needs host networking like upstream User Env, so its service stays behind the `trezor-linux` profile: +Linux needs host networking for Trezor User Env, so its service stays behind the `trezor-linux` profile: ```bash docker compose --profile trezor-linux up -d trezor-user-env-linux ``` -If an upstream `trezor-user-env.mac` or `trezor-user-env.unix` container is already running, the helper reuses it instead of creating a duplicate container with the same fixed name. - The default emulator configuration is: - model: `T2T1` @@ -88,7 +86,7 @@ Open the User Env dashboard at . Trezor Bridge listens at ## How It Works -`scripts/trezor-emulator` is the entrypoint. It starts or reuses the User Env container, then runs `scripts/trezor-controller.py` inside that container with `/trezor-user-env/.venv/bin/python3`. +`scripts/trezor-emulator` is the entrypoint. It starts this repo's Trezor User Env Compose service, then runs `scripts/trezor-controller.py` inside that container with `/trezor-user-env/.venv/bin/python3`. The Python script talks to the User Env websocket controller at `ws://127.0.0.1:9001` and sends the setup commands: diff --git a/homegate b/homegate new file mode 160000 index 0000000..887e4d5 --- /dev/null +++ b/homegate @@ -0,0 +1 @@ +Subproject commit 887e4d546088c88cbadb7e1f1dde8317a7f84140 diff --git a/homegate-admin-mock/server.js b/homegate-admin-mock/server.js new file mode 100644 index 0000000..61c2839 --- /dev/null +++ b/homegate-admin-mock/server.js @@ -0,0 +1,49 @@ +const http = require('http'); +const { randomUUID } = require('crypto'); + +const host = process.env.HOMEGATE_ADMIN_MOCK_HOST || '0.0.0.0'; +const port = Number(process.env.HOMEGATE_ADMIN_MOCK_PORT || 6289); +const adminPassword = process.env.HOMEGATE_ADMIN_MOCK_PASSWORD || 'admin'; +const pubky = + process.env.HOMEGATE_ADMIN_MOCK_PUBKY || + 'ufibwbmed6jeq9k4p583go95wofakh9fwpp4k734trq79pd9u1uy'; + +function authorized(req) { + const provided = req.headers['x-admin-password']; + return !adminPassword || provided === adminPassword; +} + +function send(res, status, body, headers = {}) { + res.writeHead(status, headers); + res.end(body); +} + +const server = http.createServer((req, res) => { + if (!authorized(req)) { + send(res, 401, 'Unauthorized\n'); + return; + } + + if (req.method === 'GET' && req.url === '/info') { + send(res, 200, JSON.stringify({ public_key: pubky }), { + 'content-type': 'application/json', + }); + return; + } + + if ( + (req.method === 'GET' || req.method === 'POST') && + req.url === '/generate_signup_token' + ) { + send(res, 200, `homegate-dev-${randomUUID()}\n`, { + 'content-type': 'text/plain', + }); + return; + } + + send(res, 404, 'Not found\n'); +}); + +server.listen(port, host, () => { + console.log(`homegate admin mock listening on ${host}:${port}`); +}); diff --git a/homegate-config.toml b/homegate-config.toml new file mode 100644 index 0000000..5a944ca --- /dev/null +++ b/homegate-config.toml @@ -0,0 +1,13 @@ +# Homegate configuration for the default bitkit-docker stack. + +database_url = "postgres://postgres:postgres@homegate-postgres:5432/pubky_homegate" +http_listen_socket = "0.0.0.0:6288" +allow_cors = true + +[homeserver] +admin_api_url = "http://homegate-admin-mock:6289" +admin_password = "admin" + +[ip_verification] +max_verifications_per_week = 2 +max_verifications_per_year = 4 diff --git a/scripts/trezor-emulator b/scripts/trezor-emulator index d5bf5f6..d34376c 100755 --- a/scripts/trezor-emulator +++ b/scripts/trezor-emulator @@ -17,7 +17,7 @@ Commands: start Start Trezor User Env, Bridge, and a deterministic T2T1 emulator status Show User Env bridge/emulator status and Bridge enumerate output adb Reverse the Bridge port for a physical Android device - stop Stop the emulator and Bridge; stop repo-managed User Env when owned here + stop Stop the emulator and Bridge; stop the repo-managed User Env service logs Tail the User Env container logs send-json Send one raw JSON command to the User Env controller help Show this help @@ -36,12 +36,10 @@ detect_service() { Darwin) TREZOR_PROFILE="${TREZOR_PROFILE:-}" TREZOR_SERVICE="${TREZOR_SERVICE:-trezor-user-env-mac}" - TREZOR_UPSTREAM_CONTAINER="${TREZOR_UPSTREAM_CONTAINER:-trezor-user-env.mac}" ;; Linux) TREZOR_PROFILE="${TREZOR_PROFILE:-trezor-linux}" TREZOR_SERVICE="${TREZOR_SERVICE:-trezor-user-env-linux}" - TREZOR_UPSTREAM_CONTAINER="${TREZOR_UPSTREAM_CONTAINER:-trezor-user-env.unix}" ;; *) echo "Unsupported OS for the bundled Trezor User Env helper: $(uname -s)" >&2 @@ -65,15 +63,6 @@ compose_with_profile() { resolve_container() { local compose_id - if [[ -n "${TREZOR_CONTAINER:-}" ]] && docker inspect "$TREZOR_CONTAINER" >/dev/null 2>&1; then - return 0 - fi - - if [[ -n "${TREZOR_UPSTREAM_CONTAINER:-}" ]] && docker inspect "$TREZOR_UPSTREAM_CONTAINER" >/dev/null 2>&1; then - TREZOR_CONTAINER="$TREZOR_UPSTREAM_CONTAINER" - return 0 - fi - compose_id="$(compose_with_profile ps -q "$TREZOR_SERVICE" 2>/dev/null | head -n 1 || true)" if [[ -n "$compose_id" ]]; then TREZOR_CONTAINER="$compose_id" @@ -83,31 +72,13 @@ resolve_container() { return 1 } -container_exists() { - resolve_container -} - container_running() { resolve_container || return 1 [[ "$(docker inspect -f '{{.State.Running}}' "$TREZOR_CONTAINER" 2>/dev/null)" == "true" ]] } -container_image() { - resolve_container || return 0 - docker inspect -f '{{.Config.Image}}' "$TREZOR_CONTAINER" 2>/dev/null || true -} - -container_project() { - resolve_container || return 0 - docker inspect -f '{{ index .Config.Labels "com.docker.compose.project" }}' "$TREZOR_CONTAINER" 2>/dev/null || true -} - container_logs() { - if container_exists; then - docker logs "$@" "$TREZOR_CONTAINER" - else - compose_with_profile logs "$@" "$TREZOR_SERVICE" - fi + compose_with_profile logs "$@" "$TREZOR_SERVICE" } prepare_dirs() { @@ -179,23 +150,16 @@ install_apple_silicon_sdl_packages() { start_env() { prepare_dirs - if container_exists; then - if [[ "$(container_image)" != "ghcr.io/trezor/trezor-user-env" ]]; then - echo "Container $TREZOR_CONTAINER already exists but is not the Trezor User Env image." >&2 - exit 1 - fi + compose_with_profile up -d "$TREZOR_SERVICE" + TREZOR_CONTAINER="$(compose_with_profile ps -q "$TREZOR_SERVICE" | head -n 1)" - if container_running; then - echo "Reusing running $TREZOR_CONTAINER container from compose project: $(container_project)" - else - echo "Starting existing $TREZOR_CONTAINER container." - docker start "$TREZOR_CONTAINER" >/dev/null - fi - else - compose_with_profile up -d "$TREZOR_SERVICE" - TREZOR_CONTAINER="$(compose_with_profile ps -q "$TREZOR_SERVICE" | head -n 1)" + if [[ -z "$TREZOR_CONTAINER" ]]; then + echo "Failed to start repo-managed Trezor User Env service: $TREZOR_SERVICE" >&2 + exit 1 fi + echo "Using repo-managed Trezor User Env service: $TREZOR_SERVICE" + wait_for_controller install_apple_silicon_sdl_packages }