import { storeJson } from '../fileModels/store.json' import { i18n } from '../i18n' import { sdk } from '../sdk' import { defaultStratumPort, defaultStratumTlsPort, logLevels, validatePorts, } from '../utils' const { InputSpec, Value } = sdk export const inputSpec = InputSpec.of({ stratumPort: Value.number({ name: i18n('Stratum Port'), description: i18n( 'Network port your miners connect to for plaintext stratum. StartOS publishes the pool on this port; if it is already claimed by another service the OS assigns a different one, so check the Stratum interface after saving. Changing this does not interrupt connected miners.', ), required: true, default: defaultStratumPort, integer: true, min: 1, max: 65535, }), stratumTlsPort: Value.number({ name: i18n('Stratum TLS Port'), description: i18n( 'Network port your miners connect to for TLS stratum. Only used when Stratum TLS is enabled below.', ), required: true, default: defaultStratumTlsPort, integer: true, min: 1, max: 65535, }), coinbaseTag: Value.text({ name: i18n('Coinbase Tag'), description: i18n( 'Short string embedded in the coinbase transaction of solved blocks.', ), required: true, default: '/Kamado/', patterns: [], }), zmqEnabled: Value.toggle({ name: i18n('ZMQ Block Notifications'), description: i18n( "Subscribe to Bitcoin Core's hashblock ZMQ topic for sub-second block detection. RPC polling remains active as a fallback either way.", ), default: true, }), tlsEnabled: Value.toggle({ name: i18n('Stratum TLS'), description: i18n( 'Accept stratum connections over TLS via an stunnel sidecar on a second port. A self-signed certificate is generated once and persisted — miners must trust the certificate (see the Stratum TLS Certificate action) or connect with verification disabled.', ), default: false, }), startDiff: Value.number({ name: i18n('Starting Difficulty'), description: i18n( 'Initial vardiff target for new miner connections. Bitaxe-class miners typically land around 16384.', ), required: true, default: 16384, integer: true, min: 1, }), minDiff: Value.number({ name: i18n('Minimum Difficulty'), description: i18n('Floor for the vardiff algorithm.'), required: true, default: 1000, integer: true, min: 1, }), maxDiff: Value.number({ name: i18n('Maximum Difficulty'), description: i18n('Ceiling for the vardiff algorithm. 0 means no cap.'), required: true, default: 0, integer: true, min: 0, }), dropIdle: Value.number({ name: i18n('Drop Idle (seconds)'), description: i18n( 'Disconnect clients that have not submitted a share in this many seconds. 0 disables the idle disconnect.', ), required: true, default: 0, integer: true, min: 0, units: i18n('seconds'), }), logLevel: Value.select({ name: i18n('Log Level'), description: i18n('Verbosity of the kamado-api log output.'), values: logLevels, default: 'info', }), mempoolExplorerUrl: Value.text({ name: i18n('Custom Block Explorer URL'), description: i18n( 'Base URL of a self-hosted mempool instance for dashboard links (e.g. https://mempool.example.com). Kamado appends /address/ and /block/, so the instance must follow the standard mempool.space URL layout. Leave empty to use the public mempool.space.', ), required: false, default: null, patterns: [ { regex: '^https?://[^\\s]+$', description: i18n( 'Must be an http:// or https:// URL with no whitespace', ), }, ], }), }) export const config = sdk.Action.withInput( // id 'config', // metadata async ({ effects }) => ({ name: i18n('Configure'), description: i18n( 'Customize vardiff, TLS, block notifications, logging, and explorer links', ), warning: null, allowedStatuses: 'any', group: null, visibility: 'enabled', }), // form input specification inputSpec, // optionally pre-fill the input form async ({ effects }) => storeJson.read().once(), // the execution function async ({ effects, input }) => { // Refuse an unusable pair (both stratum ports set to the same number). // In-container ports are fixed constants, so the user can no longer create // a bind collision — only an ambiguous external-port request. const conflict = validatePorts({ stratumPort: input.stratumPort, stratumTlsPort: input.stratumTlsPort, tlsEnabled: input.tlsEnabled, }) if (conflict) throw new Error(conflict) return storeJson.merge(effects, input) }, )