swap.lightning-network.ro LOCAL COMPANION — INITIAL BASICSWAP CONNECTOR

Run this on YOUR OWN computer, beside YOUR BasicSwap client. Never install it
on the public swap.lightning-network.ro VPS. It uses Python 3.9+ standard library only.

The public name is now swap.lightning-network.ro. Legacy internal identifiers,
environment variables and local paths retain their Liquidex names for compatibility,
so existing receipts and replay protection remain available without migration.

1. Install BasicSwap from its official instructions and verify its downloads:
   https://basicswapdex.com/docs/user-guides/install
   The companion does not install or update BasicSwap, create/unlock wallets,
   read seed phrases, start daemons or alter your BasicSwap configuration.
2. Set up mainnet BTC/XMR and/or LTC/XMR wallets. You may use BasicSwap's
   documented light modes, with their privacy and validation tradeoffs:
   https://basicswapdex.com/docs/user-guides/lightweight-modes
3. Keep BasicSwap's HTTP interface private on 127.0.0.1:12700 (or another local
   port). Enable its documented authentication; do not expose its API publicly:
   https://basicswapdex.com/docs/user-guides/web-ui-authentication
4. Unpack this directory, inspect the source, then run in a terminal:

   python3 -B companion.py --ask-password

   Enter YOUR local BasicSwap API password at the private prompt. It is not
   printed, sent to the website/browser, or written to the receipt journal.
   If no API password is configured, run `python3 -B companion.py` instead.
   LIQUIDEX_BASICSWAP_PASSWORD is an alternative environment input, but a
   private prompt avoids putting secrets into shell history. There is no
   password command-line argument. --basicswap-port accepts a numeric LOCAL
   port only; it cannot point to a remote server or public wallet.
5. Open http://127.0.0.1:17700/ and check the local mainnet status. From a fresh
   compatible swap.lightning-network.ro quote, open the local proposal. Review and approve it
   on this separate local page. A website proposal alone cannot create a bid.

WHAT IT DOES

Initial pairs: native BTC <-> XMR and LTC <-> XMR on mainnet, via ONE public
BasicSwap offer. No chain swapping, Lightning, other engines or stablecoins.
Your taker source is BasicSwap offer.coin_to; your receive is offer.coin_from.
The API's amount_from is therefore what YOU RECEIVE, not what you spend.

The companion fetches the actual local offer and checks mainnet, direction,
status, capacity, minimum size, fixed/negotiable amount terms, both wallets'
lock/seed/sync state, and confirmed source funds. It calculates in native
atomic units and checks native rate rounding compatibility. Exact unsupported
amounts fail closed. BasicSwap still performs its own funding and fee checks.

The page displays actual reviewed principal, gross receive, minimum gross
receive, fee rates, offer expiry and wallet connection modes. The proposal's
gross minimum can include the explicitly disclosed website tolerance (currently
0.5%). This is NOT an all-in price guarantee: source network fees are additional,
and redemption fees reduce receive. Total fees/transaction sizes are not fully
known here. This initial connector needs native-client comparison and a funded
testnet integration exercise before it should be treated as production ready.

After your explicit local confirmation it rechecks terms and sends exactly one
bid with explicit maker amount_from, taker amount_to and 10-minute bid validity.
A sent bid is not maker acceptance or swap completion. Any changed terms require
a new review. No arbitrary API method, URL, withdrawal, destination address,
debug switch, fee bypass, wallet-unlock or recovery-signing endpoint exists.

RUNNING AND RECOVERY

The listener binds only to 127.0.0.1:17700. Ctrl-C stops this companion, not
BasicSwap. The companion has no daemon/wallet lifecycle control. BasicSwap's
engine must stay available to progress protocol transactions and recover funds;
an offline engine cannot guarantee timely refunds. Closing the public swap.lightning-network.ro
website does not affect an already-submitted bid. Continue in your native
BasicSwap interface using the saved bid link.

Private local submission receipts live in:
  ~/.local/share/liquidex-companion/receipts.json
You can override this with --state-dir. Keep this directory private. It stores
request/offer/bid identifiers, amounts and status, never wallet keys/passwords.
There are no companion access logs. BasicSwap and your operating system have
their own logs and network activity. The local browser page has no remote
scripts, trackers, analytics or RPC requests; external links are user-opened.

A durable uncertain marker is written BEFORE attempting a bid. If the response
times out, the process crashes, or the result is ambiguous, the same request is
blocked across restart. Inspect BasicSwap SENT BIDS before taking any further
action; do not delete the journal to bypass this protection. A new proposal is
not proof the prior swap was cancelled. If a receipt is marked submitted, use
its bid link to see actual protocol state in BasicSwap.

Your BasicSwap database and wallet backups are independent of this journal.
The seed alone is insufficient to recover active swaps. Follow official backup
and restore instructions; do not stop an active engine merely to follow an
automated backup recipe:
  https://basicswapdex.com/docs/user-guides/backup-and-restore

BROWSER BOUNDARIES

The website opens a new local tab with an expiring, public trade proposal in
the URL FRAGMENT. The local script removes that fragment immediately and posts
the proposal to its own loopback server. Queries and cross-origin mutations
are rejected. Strict Host/Origin checks, no CORS, SameSite/HttpOnly session cookie,
CSRF tokens, CSP/frame denial, bounded bodies, 45-second session-bound one-use
approvals, fresh-term comparison, and durable replay prevention protect this
boundary. The public website cannot read wallet balances or submit a bid itself.
The API password exists only in the user-owned Python process.

These controls do not protect a compromised computer, browser, BasicSwap binary,
or a user approving malicious terms. This is source code, not a signed installer.
Inspect the supplied checksum via a separately trusted channel; a checksum
served beside a download does not establish authenticity by itself.

DEVELOPMENT / VERIFIED REFERENCES

Run mock-only tests (no wallet, no funds, no real BasicSwap endpoint):
  python3 -B test_companion.py
From the repository root:
  node --test tests/companion.test.cjs

The separate browser fixture (requires already-installed Playwright/Chrome)
exercises the actual bundled UI and Python service against an ephemeral fake
BasicSwap server, including submission, browser reload, process restart, and an
unknown bid response. It never contacts port 12700 or an installed wallet:
  node tests/browser-companion-qa.cjs --playwright-module /path/to/playwright

HTTP tests bind a temporary loopback port and use an in-memory fake BasicSwap.
They verify amount direction, atomic precision, mainnet/readiness failure,
rate/fee changes, expiry, one-use and session binding, simultaneous double-clicks,
durable unknown-outcome replay prevention, before-send disk errors, hostile
Host/Origin/CSRF, no CORS, and read-only page/health behavior.

Official API/source reviewed 2026-11-07 (upstream master; APIs may change):
  https://basicswapdex.com/docs/api
  https://github.com/basicswap/basicswap/blob/master/basicswap/js_server.py
    js_index/getSummary: explicit network; js_offers: with_extra_info and maker
    direction; js_wallets: local wallet state; js_bids/new: amount_from/amount_to.
  https://github.com/basicswap/basicswap/blob/master/basicswap/basicswap.py
    setBidAmounts/postXmrBid: partial fixed-rate bids can be auto-adjusted.
    The companion picks a native-rounding-stable amount before review.
  https://github.com/basicswap/basicswap/blob/master/basicswap/util/__init__.py
    make_int/float_to_str: rounding compatibility check only. All bounds and
    coin accounting in the companion are exact integer arithmetic.

The mock tests and internal code review are not an independent professional
security audit or evidence of live swap success. Real integration testing must
be done separately with a deliberately configured test environment. This
mainnet-only build intentionally refuses testnet rather than guessing a network.
