Skip to content

jmwalletd.cli

jmwalletd.cli

CLI entry point for the JoinMarket wallet daemon.

Usage::

jmwalletd [--host HOST] [--port PORT] [--ws-port WS_PORT] [--data-dir DIR] [--no-tls]

Attributes

app = typer.Typer(name='jmwalletd', help='JoinMarket wallet daemon - JAM-compatible HTTP/WebSocket API.') module-attribute

Functions:

main() -> None

Entry point for the jmwalletd console script.

Source code in jmwalletd/src/jmwalletd/cli.py
153
154
155
def main() -> None:
    """Entry point for the ``jmwalletd`` console script."""
    app()

serve(host: Annotated[str, typer.Option(envvar='JMWALLETD_HOST', help='Bind address')] = '127.0.0.1', port: Annotated[int, typer.Option(help='HTTPS/HTTP port')] = 28183, ws_port: Annotated[int, typer.Option(help='WebSocket port (0 = same as HTTP)')] = 0, data_dir: Annotated[Path | None, typer.Option(envvar='JOINMARKET_DATA_DIR', help='Data directory (default: ~/.joinmarket-ng or $JOINMARKET_DATA_DIR)')] = None, config_file: Annotated[Path | None, typer.Option('--config-file', envvar='JOINMARKET_CONFIG_FILE', help='Config file path (decoupled from data dir). Defaults to <data-dir>/config.toml')] = None, no_tls: Annotated[bool, typer.Option(envvar='JMWALLETD_NO_TLS', help='Disable TLS (plain HTTP)')] = False) -> None

Start the wallet daemon HTTP/WebSocket server.

Source code in jmwalletd/src/jmwalletd/cli.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
@app.command()
def serve(
    host: Annotated[str, typer.Option(envvar="JMWALLETD_HOST", help="Bind address")] = "127.0.0.1",
    port: Annotated[int, typer.Option(help="HTTPS/HTTP port")] = 28183,
    ws_port: Annotated[int, typer.Option(help="WebSocket port (0 = same as HTTP)")] = 0,
    data_dir: Annotated[
        Path | None,
        typer.Option(
            envvar="JOINMARKET_DATA_DIR",
            help="Data directory (default: ~/.joinmarket-ng or $JOINMARKET_DATA_DIR)",
        ),
    ] = None,
    config_file: Annotated[
        Path | None,
        typer.Option(
            "--config-file",
            envvar="JOINMARKET_CONFIG_FILE",
            help="Config file path (decoupled from data dir). Defaults to <data-dir>/config.toml",
        ),
    ] = None,
    no_tls: Annotated[
        bool, typer.Option(envvar="JMWALLETD_NO_TLS", help="Disable TLS (plain HTTP)")
    ] = False,
) -> None:
    """Start the wallet daemon HTTP/WebSocket server."""
    import os
    import ssl

    import uvicorn
    from loguru import logger

    from jmcore.paths import get_default_data_dir
    from jmcore.process_hardening import harden_current_process
    from jmwalletd.app import create_app

    # Disable core dumps and ptrace before loading wallet secrets.
    harden_current_process()

    # Honour an explicit --config-file so the daemon reads (and creates) its
    # config outside the data dir when requested (FHS deployments, issue #537).
    if config_file is not None:
        os.environ["JOINMARKET_CONFIG_FILE"] = str(Path(config_file).expanduser())

    resolved_data_dir = data_dir or get_default_data_dir()
    resolved_data_dir.mkdir(parents=True, exist_ok=True)

    fast_app = create_app(data_dir=resolved_data_dir)

    ssl_context: ssl.SSLContext | None = None
    if not no_tls:
        ssl_dir = resolved_data_dir / "ssl"
        cert_file = ssl_dir / "cert.pem"
        key_file = ssl_dir / "key.pem"

        if not cert_file.exists() or not key_file.exists():
            _generate_self_signed_cert(ssl_dir)

        ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
        ssl_context.load_cert_chain(str(cert_file), str(key_file))

    scheme = "http" if no_tls else "https"
    logger.info("Starting jmwalletd on {}://{}:{}", scheme, host, port)

    uvicorn.run(
        fast_app,
        host=host,
        port=port,
        ssl_certfile=str(resolved_data_dir / "ssl" / "cert.pem") if not no_tls else None,
        ssl_keyfile=str(resolved_data_dir / "ssl" / "key.pem") if not no_tls else None,
        log_level="info",
    )