import { storeJson } from './fileModels/store.json' import { i18n } from './i18n' import { sdk } from './sdk' import { defaultStratumPort, defaultStratumTlsPort, stratumHostId, stratumInternalPort, stratumTlsHostId, stratumTlsInternalPort, uiHostId, uiPort, } from './utils' export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { // The user's stratum ports are EXTERNAL ports only. Read reactively so // changing them in the Configure action re-runs this and updates the // binding — the same mechanism that adds/removes the TLS interface when TLS // is toggled. The in-container ports stay fixed (see utils.ts), so each // change updates the existing binding rather than orphaning it. const ports = await storeJson .read((s) => ({ stratum: s.stratumPort, stratumTls: s.stratumTlsPort, tlsEnabled: s.tlsEnabled, })) .const(effects) const externalStratumPort = ports?.stratum ?? defaultStratumPort const externalStratumTlsPort = ports?.stratumTls ?? defaultStratumTlsPort // Web dashboard const uiMulti = sdk.MultiHost.of(effects, uiHostId) const uiMultiOrigin = await uiMulti.bindPort(uiPort, { protocol: 'http', }) const ui = sdk.createInterface(effects, { name: i18n('Web Dashboard'), id: 'ui', description: i18n( 'Real-time Kamado Pool dashboard (hashrate, miners, blocks, best shares)', ), type: 'ui', masked: false, schemeOverride: null, username: null, path: '', query: {}, }) const uiReceipt = await uiMultiOrigin.export([ui]) const receipts = [uiReceipt] // Plaintext stratum — a raw TCP interface. Unlike StartOS 0.3.x, 0.4.0 // forwards raw TCP on the LAN, so miners connect directly to the host at // the assigned external port; no router forward or simpleproxy needed. const stratumMulti = sdk.MultiHost.of(effects, stratumHostId) const stratumOrigin = await stratumMulti.bindPort(stratumInternalPort, { protocol: null, preferredExternalPort: externalStratumPort, addSsl: null, secure: { ssl: false }, }) const stratum = sdk.createInterface(effects, { name: i18n('Stratum'), id: 'stratum', description: i18n( 'Plaintext stratum endpoint. Point miners here with their Bitcoin payout address as the username', ), type: 'api', masked: false, schemeOverride: { ssl: 'stratum+ssl', noSsl: 'stratum+tcp' }, username: null, path: '', query: {}, }) receipts.push(await stratumOrigin.export([stratum])) // TLS stratum (conditional) — stunnel terminates TLS with the persisted // package-managed certificate and forwards to ckpool's loopback-only // second bind. The OS sees a raw TCP port; TLS lives at the app layer, so // the noSsl scheme is deliberately stratum+ssl. if (ports?.tlsEnabled) { const tlsMulti = sdk.MultiHost.of(effects, stratumTlsHostId) const tlsOrigin = await tlsMulti.bindPort(stratumTlsInternalPort, { protocol: null, preferredExternalPort: externalStratumTlsPort, addSsl: null, secure: { ssl: false }, }) const stratumTls = sdk.createInterface(effects, { name: i18n('Stratum (TLS)'), id: 'stratum-tls', description: i18n( 'TLS-encrypted stratum endpoint (self-signed certificate — see the Stratum TLS Certificate action)', ), type: 'api', masked: false, schemeOverride: { ssl: 'stratum+ssl', noSsl: 'stratum+ssl' }, username: null, path: '', query: {}, }) receipts.push(await tlsOrigin.export([stratumTls])) } // Drop bindings we no longer use — primarily the stratum-tls binding after // the user disables TLS. With fixed internal ports this no longer has to // clean up after port changes (the whole point of keeping them fixed), but // it still matters for the TLS toggle, and it clears orphans left by older // versions of this package that did move the internal port. await sdk.clearBindings(effects, { except: [ { id: uiHostId, internalPort: uiPort }, { id: stratumHostId, internalPort: stratumInternalPort }, ...(ports?.tlsEnabled ? [{ id: stratumTlsHostId, internalPort: stratumTlsInternalPort }] : []), ], }) // Exported service interfaces are a SECOND registry, independent of the // bindings above and with its own cleanup effect. Clearing bindings alone // leaves an orphaned interface record behind, which the UI still lists — so // a port change can produce two identical "Stratum" rows. Excluding // 'stratum-tls' when TLS is off also removes that row when the user // disables TLS, rather than leaving a dead endpoint on display. await effects.clearServiceInterfaces({ except: ['ui', 'stratum', ...(ports?.tlsEnabled ? ['stratum-tls'] : [])], }) return receipts })