157 lines
4.7 KiB
TypeScript
157 lines
4.7 KiB
TypeScript
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/<addr> and /block/<hash>, 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)
|
|
},
|
|
)
|