Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
59 changes: 57 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
```
Expand All @@ -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.

Expand Down Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
60 changes: 59 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -276,5 +332,7 @@ volumes:
bitcoin_home:
postgres_data:
lnurl_auth_data:
homegate_postgres_data:
homegate_data:

networks: {}
6 changes: 2 additions & 4 deletions docs/trezor-emulator.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -88,7 +86,7 @@ Open the User Env dashboard at <http://localhost:9002>. 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:

Expand Down
1 change: 1 addition & 0 deletions homegate
Submodule homegate added at 887e4d
49 changes: 49 additions & 0 deletions homegate-admin-mock/server.js
Original file line number Diff line number Diff line change
@@ -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}`);
});
13 changes: 13 additions & 0 deletions homegate-config.toml
Original file line number Diff line number Diff line change
@@ -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
Comment on lines +12 to +13
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the reason for adding homegate is to be able to test profile creation I'd increase those a lot, otherwise it'll allow creating 2 profiles per session I believe.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also does it make sense to run this without a password?

Loading