Skip to content
Merged
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
10 changes: 8 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@ verified, tried, and decided belongs in the commit message and the PR body.

## This repo

- **`albyhub-startos` depends on this package** and imports the `manifest` export from `startos/manifest`, plus `apiHostId`/`apiInterfaceId` from `startos/interfaces.ts`. Treat those three as a public API: renaming one is a cross-repo change.
- **phoenixd owns `phoenix.conf` — don't model or template it.** It generates the file on first start, including the `http-password`. A `FileHelper` over it would fight the daemon for ownership and could clobber a live credential.
- **`albyhub-startos` depends on this package** and imports the `manifest` export from `startos/manifest`, plus `apiHostId`/`apiInterfaceId` from `startos/interfaces.ts`. Treat those three as a public API: renaming one is a cross-repo change. Going the other way, this package imports host ids and ports from `electrs-startos` and `fulcrum-startos` — scalars only, so nothing of theirs ships in the s9pk. It also reads `http-password` straight out of `phoenix.conf` on the mounted volume — so that key stays in that file under that name, whoever wrote it.
- **`phoenix.conf`'s model must never let a value reach the serializer as `undefined`.** `FileHelper.env` in start-sdk 2.0.9 has no `filterUndefined`, so such a key lands on disk as the literal string `undefined` and phoenixd fails to parse it on the next boot. The `onWrite` transformer in `startos/fileModels/phoenix.conf.ts` is what prevents that. Start9Labs/start-technologies#3874 fixes it in the SDK — drop the transformer when this package moves to a release carrying that fix, not before.
- **`electrum-server` is derived, never typed straight into `phoenix.conf`.** The user's choice is StartOS state in `store.json`; `init/watchChainSource.ts` resolves it to an address. Resolve an indexer's **TLS** bridge address (`ssl: true`) — phoenixd hardcodes `TLS.TRUSTED_CERTIFICATES()` and cannot speak to the plaintext bridge port.
- **The `trust-startos-ca` oneshot is what makes an on-server indexer reachable at all**, by putting this server's root CA where phoenixd's rustls looks (`/etc/ssl/certs/ca-certificates.crt`). Remove it and every non-public chain source fails its TLS handshake.
- **`getRootCa` in `startos/utils.ts` is a stand-in for `sdk.getRootCa`**, which lands in start-sdk 2.0.10. Delete it for the SDK call when this package moves to that release.
- **Keep the shape's `.catchall(z.string())`.** phoenixd appends `http-password-limited-access` and `webhook-secret` to the same file, and a shape that dropped unknown keys would delete them on the next write.
- **A conf key that changes daemon behavior belongs in the reactive read in `main.ts`.** phoenixd parses the file only at startup, so a key an action can write but `main.ts` does not watch takes effect at some unrelated later restart.
- **`seed.dat` is phoenixd's.** Don't model it, and don't add an action that reads it out.
- **The `main` volume is a wallet**, holding `seed.dat` and the channel database. Channel balances are not recoverable from the seed alone, so never exclude anything from the backup and never suggest running a restored copy alongside the original.
- **The `chown` oneshot is required** — the image runs as `phoenix` and the volume arrives root-owned.
- **No local Bitcoin node.** Channels are managed by ACINQ's LSP; don't add bitcoind as a dependency or imply one is needed.
146 changes: 103 additions & 43 deletions README.md

Large diffs are not rendered by default.

46 changes: 37 additions & 9 deletions instructions.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# phoenixd

phoenixd is API-only — there is no web UI. You drive it from the **Server API** interface or with the bundled `phoenix-cli`, authenticating with the HTTP password phoenixd generates on first run.
phoenixd is API-only — there is no web UI. You drive it from the **Server API** interface or with the bundled `phoenix-cli`, authenticating with an API password you set from StartOS.

## Documentation

- [Start9 Bitcoin Guides](https://docs.start9.com/bitcoin-guides/) — connecting wallets to a Lightning node on StartOS.
- [phoenixd server docs](https://phoenix.acinq.co/server) — the upstream operator guide: configuration, `phoenix-cli` usage, and how channels and fees work.
- [phoenixd HTTP API reference](https://phoenix.acinq.co/server/api) — every endpoint, its parameters, and the webhook payloads.
- [phoenixd server docs](https://phoenix.acinq.co/server/*) — the upstream operator guide and the HTTP API reference: configuration, `phoenix-cli` usage, every endpoint, and how channels and fees work.

## What you get on StartOS

Expand All @@ -15,15 +14,39 @@ phoenixd is API-only — there is no web UI. You drive it from the **Server API*

## Getting set up

1. Start the service. On first launch phoenixd creates a fresh wallet seed and writes `phoenix.conf` (including the `http-password`) into the data volume.
2. Retrieve the HTTP password from `phoenix.conf` — open the service's terminal from the **Dashboard** and run `cat /phoenix/.phoenix/phoenix.conf`. Save the `http-password` value to your password manager; every API call and every `phoenix-cli` invocation needs it.
1. Start the service.
2. Run **Set API Password**. StartOS raises this as a task, because it is the only way anyone learns a password for the API — nothing can read one back to you afterwards. Save it to your password manager.
3. The seed phrase is written to `seed.dat` inside the data volume and is **not** shown in the StartOS UI. If you want it on paper, retrieve it through the service's terminal: `cat /phoenix/.phoenix/seed.dat`. Lose this seed and you lose access to any on-chain balance not covered by channel state.

## Actions

### Set API Password

Sets the password that every API call and `phoenix-cli` invocation needs, and shows it once. Run it again at any time to rotate. Anything already using the old password — Alby Hub, your own scripts — has to be given the new one.

### Set Chain Source

Chooses where phoenixd watches the blockchain from. Whoever runs that server sees the on-chain addresses this wallet deposits to and withdraws from, so this is a real privacy choice.

- **ACINQ's public servers** — the default. ACINQ picks one for you.
- **Electrs on this server** / **Fulcrum on this server** — your own indexer. Install and sync it first; phoenixd will then require it to be running.
- **Another Electrum server** — a `host:port` you type in. It has to serve TLS with a certificate this server trusts; phoenixd has no plaintext or self-signed option.

### Set Liquidity Policy

Caps what phoenixd spends buying inbound liquidity from ACINQ.

- **Automatic Liquidity** — how much room phoenixd buys when a payment does not fit your current channel. Turning it off makes phoenixd reject those payments instead.
- **Max Mining Fee** — the most it will spend on mining fees for one on-chain operation.
- **Max Fee Credit** — how much ACINQ may hold on your behalf for payments too small to pay for a channel. That credit is **non-refundable**, and payments are rejected once the ceiling is reached.

Left alone, these are phoenixd's own defaults, which do spend without asking.

## Using phoenixd

### Server API

Open the **Server API** interface to copy the address phoenixd listens on. All endpoints require HTTP basic auth with **an empty username** and the password from `phoenix.conf`. For example, to fetch node info from another machine on your LAN:
Open the **Server API** interface to copy the address phoenixd listens on. All endpoints require HTTP basic auth with **an empty username** and your API password. For example, to fetch node info from another machine on your LAN:

```
curl -u :<password> http://<server-api-address>/getinfo
Expand All @@ -36,8 +59,13 @@ See the upstream docs for the full endpoint list (`/createinvoice`, `/payinvoice
The image ships with `phoenix-cli`, which speaks to the same HTTP API. Open the service's terminal from the **Dashboard** and run, for example:

```
phoenix-cli --http-password=<password> getinfo
phoenix-cli --http-password=<password> createinvoice --amountSat=1000 --description="test"
phoenix-cli getinfo
phoenix-cli createinvoice --amountSat=1000 --description="test"
```

`phoenix-cli` reads `phoenix.conf` by default, so inside the container you can usually omit `--http-password` entirely.
Inside the container `phoenix-cli` reads the password from the configuration itself, so you do not need to pass it.

## Health checks

- **primary daemon** — the API is listening.
- **Node** — phoenixd has reached its Electrum server, with the current block height and how many channels are open. A brand-new wallet has none until a Lightning payment arrives and ACINQ opens one.
220 changes: 219 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"check": "tsc --noEmit"
},
"dependencies": {
"@start9labs/start-sdk": "2.0.9"
"@start9labs/start-sdk": "2.0.9",
"electrs-startos": "github:Start9Labs/electrs-startos#next",
"fulcrum-startos": "github:Start9Labs/fulcrum-startos#next"
},
"devDependencies": {
"@types/node": "^22.19.0",
Expand Down
6 changes: 6 additions & 0 deletions startos/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import { sdk } from '../sdk'
import { setApiPassword } from './setApiPassword'
import { setChainSource } from './setChainSource'
import { setLiquidityPolicy } from './setLiquidityPolicy'

export const actions = sdk.Actions.of()
.addAction(setApiPassword)
.addAction(setChainSource)
.addAction(setLiquidityPolicy)
Loading