the owner's work, committed as one unit rather than split: the registration
files (App.tsx, Dock, AppRegistry, hono.ts, the schema and db barrels,
ecosystem.config.cjs) all reference modules under src/servers/{api,sidecar}/wallet
and src/workspaces/officerdev/src/apps/Wallet, so committing the shared plumbing
on its own would leave a commit that does not build.
officer-wallet is a new pm2 peer holding seed material sealed under an owner
passphrase on top of VAULT_STORE_KEY, with an unlock ttl after which the root key
is wiped from memory. five backends: on-chain via esplora, and lnd, clnrest,
lndhub and nwc for lightning. bolt11 encode/decode is implemented in-tree.
no secrets in the diff — the key-shaped literals under sidecar/wallet are the
bolt11 spec vectors and the bip39 "abandon … about" vector. .env.example gains
placeholders only. bun test src/servers/sidecar/wallet: 38 pass, 0 fail.
not reviewed line by line; assembled and verified to build, not audited.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
80 lines
4.7 KiB
Bash
80 lines
4.7 KiB
Bash
PORT=9000
|
|
JWT_SECRET="<generate with: openssl rand -base64 32>"
|
|
POSTGRES_URL="postgres://postgres:password@localhost:5432/officer"
|
|
MAIL_TRANSPORT="smtp://localhost:1025"
|
|
PUBLIC_URL=http://localhost:9000
|
|
|
|
# Guards (CORS origin checks, rate limits, password-strength rules) are ON unless this is set to
|
|
# "dev" or "development". Leave it unset or set it to "production" for a real deployment; only set
|
|
# it to "dev" on a local machine you trust, since that disables all three.
|
|
PUBLIC_BUILD_ENV=production
|
|
|
|
DATA_PATH=/path/to/data
|
|
OFFICER_ITEMS_DIR=/path/to/officer-items
|
|
HOME_DIR=/home/user
|
|
BROWSER_RELAY_PORT=18792
|
|
|
|
# ── Sidecars ────────────────────────────────────────────────────────────────────────────────────
|
|
# Each sidecar owns its upstream's credentials; the platform API is only a thin auth+forward proxy
|
|
# and never sees them. An unset upstream URL is not fatal — the sidecar logs a warning at boot and
|
|
# answers 503 until it is set, so you can run Officer with any subset of these configured.
|
|
|
|
# Transmission (officer-transmission). TRANSMISSION_USER/PASS are only needed if the daemon has RPC
|
|
# auth turned on; leave them empty otherwise, since Transmission rejects an empty Basic header.
|
|
# TRANSMISSION_RPC_PATH defaults to /transmission/rpc and only needs setting behind a reverse proxy
|
|
# that mounts the RPC endpoint somewhere else.
|
|
TRANSMISSION_URL=http://127.0.0.1:9091
|
|
TRANSMISSION_USER=
|
|
TRANSMISSION_PASS=
|
|
# TRANSMISSION_RPC_PATH=/transmission/rpc
|
|
|
|
# InvoiceShelf (officer-invoiceshelf). The token is a Sanctum personal access token — mint one with
|
|
# POST /api/v1/auth/login {username: <email>, password, device_name} and copy the `token` field. It has
|
|
# full abilities and never expires, so treat it as a password.
|
|
# INVOICESHELF_COMPANY_ID pins which company every request is scoped to. Leave it unset on a
|
|
# single-company install and the sidecar resolves it once at boot and LOGS the choice — worth setting
|
|
# explicitly if you have more than one, because InvoiceShelf does not error on a wrong company header,
|
|
# it silently returns the other company's data.
|
|
INVOICESHELF_URL=https://invoice.example.com
|
|
INVOICESHELF_TOKEN="<sanctum api token, e.g. 1|xxxxxxxx>"
|
|
# INVOICESHELF_COMPANY_ID=1
|
|
|
|
# slskd (officer-slskd). The key is injected as X-API-Key on every forwarded request.
|
|
SLSKD_URL=http://127.0.0.1:5030
|
|
SLSKD_API_KEY="<slskd api key>"
|
|
|
|
# Vaultwarden (officer-vault). VAULT_STORE_KEY encrypts stored secrets at rest — any strong secret
|
|
# of 16+ chars works, and CHANGING IT MAKES EXISTING STORED SECRETS UNREADABLE.
|
|
VAULTWARDEN_URL=http://127.0.0.1:8222
|
|
VAULT_STORE_KEY="<generate with: openssl rand -base64 32>"
|
|
|
|
# Anthropic proxy (officer-anthropic-proxy). Defaults to 5051; it holds the API credential, which
|
|
# lives in the host env rather than here.
|
|
# ANTHROPIC_PROXY_PORT=5051
|
|
|
|
# ReClip — the self-hosted yt-dlp service the download-media capability talks to. Defaults to
|
|
# http://localhost:8899.
|
|
# RECLIP_URL=http://localhost:8899
|
|
|
|
# ── Headscale (/api/vpn) ────────────────────────────────────────────────────────────────────────
|
|
# These drive the /api/vpn router, NOT the officer-headscale sidecar. The sidecar deliberately reads
|
|
# neither, keeping its registered servers and their keys in Postgres so host env can never shadow
|
|
# one. Set these only if you use /api/vpn.
|
|
# HEADSCALE_URL=https://headscale.example.com
|
|
# HEADSCALE_API_KEY="<headscale admin api key>"
|
|
# HEADSCALE_USER=officer
|
|
|
|
# ── Bitcoin wallet (officer-wallet) ─────────────────────────────────────────────────────────────
|
|
# Chain data source for the self-custodial on-chain wallet. Any Esplora-compatible API works —
|
|
# mempool.space by default, or point it at your own node's esplora/electrs when you run one.
|
|
# WALLET_ESPLORA_URL=https://mempool.space/api
|
|
# WALLET_NETWORK=bitcoin # bitcoin | testnet | signet | regtest
|
|
#
|
|
# How long an unlocked wallet stays unlocked, in seconds. Default 900 (15 min). The root key is held
|
|
# in the sidecar's memory for exactly this long after an unlock, then wiped. Shorter is safer.
|
|
# WALLET_UNLOCK_TTL_SEC=900
|
|
#
|
|
# NOTE: seed material is encrypted with VAULT_STORE_KEY (above) on top of the owner passphrase that
|
|
# seals it. Both are required to spend. If you lose VAULT_STORE_KEY, every stored seed is
|
|
# unrecoverable — back up the mnemonics separately, offline.
|