From 3af5c058074f278ba377d41b93988c2348bac7d7 Mon Sep 17 00:00:00 2001 From: Brian Botha Date: Thu, 20 Jul 2023 17:39:48 +1000 Subject: [PATCH] WIP --- src/PolykeyAgent.ts | 56 ++++----- src/PolykeyClient.ts | 2 +- src/bootstrap/utils.ts | 12 +- src/config.ts | 180 +++++++++++++++++----------- src/nodes/NodeConnectionManager.ts | 2 - src/schema/Schema.ts | 2 +- src/vaults/VaultManager.ts | 2 +- tests/PolykeyAgent.test.ts | 34 +++--- tests/PolykeyClient.test.ts | 2 +- tests/bootstrap/utils.test.ts | 24 ++-- tests/client/handlers/agent.test.ts | 4 +- tests/status/Status.test.ts | 48 ++++---- 12 files changed, 205 insertions(+), 163 deletions(-) diff --git a/src/PolykeyAgent.ts b/src/PolykeyAgent.ts index 6ead0d885..d268d5bea 100644 --- a/src/PolykeyAgent.ts +++ b/src/PolykeyAgent.ts @@ -46,26 +46,6 @@ import TaskManager from './tasks/TaskManager'; import { serverManifest as clientServerManifest } from './client/handlers'; import { serverManifest as agentServerManifest } from './agent/handlers'; -type NetworkConfig = { - // Agent QUICSocket config - agentHost?: string; - agentPort?: number; - ipv6Only?: boolean; - agentKeepAliveIntervalTime?: number; - agentMaxIdleTimeout?: number; - // RPCServer for client service - clientHost?: string; - clientPort?: number; - // Websocket server config - maxIdleTimeout?: number; - pingIntervalTime?: number; - pingTimeoutTimeTime?: number; - // RPC config - clientParserBufferByteLimit?: number; - handlerTimeoutTime?: number; - handlerTimeoutGraceTime?: number; -}; - interface PolykeyAgent extends CreateDestroyStartStop {} @CreateDestroyStartStop( new errors.ErrorPolykeyAgentRunning(), @@ -123,6 +103,10 @@ class PolykeyAgent { }: { password: string; nodePath?: string; + + // WHY IS THERE SO MANY CONFIGURATIONS??? + + keyRingConfig?: { recoveryCode?: RecoveryCode; privateKey?: PrivateKey; @@ -142,7 +126,25 @@ class PolykeyAgent { connectionHolePunchTimeoutTime?: number; connectionHolePunchIntervalTime?: number; }; - networkConfig?: NetworkConfig; + networkConfig?: { + // Agent QUICSocket config + agentHost?: string; + agentPort?: number; + ipv6Only?: boolean; + agentKeepAliveIntervalTime?: number; + agentMaxIdleTimeout?: number; + // RPCServer for client service + clientHost?: string; + clientPort?: number; + // Websocket server config + maxIdleTimeout?: number; + pingIntervalTime?: number; + pingTimeoutTimeTime?: number; + // RPC config + clientParserBufferByteLimit?: number; + handlerTimeoutTime?: number; + handlerTimeoutGraceTime?: number; + }; seedNodes?: SeedNodes; workers?: number; status?: Status; @@ -192,12 +194,12 @@ class PolykeyAgent { }; await utils.mkdirExists(fs, nodePath); - const statusPath = path.join(nodePath, config.defaults.statusBase); - const statusLockPath = path.join(nodePath, config.defaults.statusLockBase); - const statePath = path.join(nodePath, config.defaults.stateBase); - const dbPath = path.join(statePath, config.defaults.dbBase); - const keysPath = path.join(statePath, config.defaults.keysBase); - const vaultsPath = path.join(statePath, config.defaults.vaultsBase); + const statusPath = path.join(nodePath, config.paths.statusBase); + const statusLockPath = path.join(nodePath, config.paths.statusLockBase); + const statePath = path.join(nodePath, config.paths.stateBase); + const dbPath = path.join(statePath, config.paths.dbBase); + const keysPath = path.join(statePath, config.paths.keysBase); + const vaultsPath = path.join(statePath, config.paths.vaultsBase); const events = new EventBus({ captureRejections: true, }); diff --git a/src/PolykeyClient.ts b/src/PolykeyClient.ts index 17b7bc046..c6b0afecb 100644 --- a/src/PolykeyClient.ts +++ b/src/PolykeyClient.ts @@ -49,7 +49,7 @@ class PolykeyClient { throw new errors.ErrorUtilsNodePath(); } await utils.mkdirExists(fs, nodePath); - const sessionTokenPath = path.join(nodePath, config.defaults.tokenBase); + const sessionTokenPath = path.join(nodePath, config.paths.tokenBase); session = session ?? (await Session.createSession({ diff --git a/src/bootstrap/utils.ts b/src/bootstrap/utils.ts index b6659c493..5783b1b1f 100644 --- a/src/bootstrap/utils.ts +++ b/src/bootstrap/utils.ts @@ -56,12 +56,12 @@ async function bootstrapState({ } await mkdirExists(fs, nodePath); // Setup node path and sub paths - const statusPath = path.join(nodePath, config.defaults.statusBase); - const statusLockPath = path.join(nodePath, config.defaults.statusLockBase); - const statePath = path.join(nodePath, config.defaults.stateBase); - const dbPath = path.join(statePath, config.defaults.dbBase); - const keysPath = path.join(statePath, config.defaults.keysBase); - const vaultsPath = path.join(statePath, config.defaults.vaultsBase); + const statusPath = path.join(nodePath, config.paths.statusBase); + const statusLockPath = path.join(nodePath, config.paths.statusLockBase); + const statePath = path.join(nodePath, config.paths.stateBase); + const dbPath = path.join(statePath, config.paths.dbBase); + const keysPath = path.join(statePath, config.paths.keysBase); + const vaultsPath = path.join(statePath, config.paths.vaultsBase); const status = new Status({ statusPath, statusLockPath, diff --git a/src/config.ts b/src/config.ts index 4de7c276d..a2ad09825 100644 --- a/src/config.ts +++ b/src/config.ts @@ -73,10 +73,9 @@ const config = { }, }, /** - * Default configuration + * File/directory paths */ - defaults: { - nodePath: getDefaultNodePath(), + paths: { statusBase: 'status.json', statusLockBase: 'status.lock', stateBase: 'state', @@ -86,73 +85,116 @@ const config = { vaultsBase: 'vaults', efsBase: 'efs', tokenBase: 'token', - certManagerConfig: { - certDuration: 31536000, - }, - networkConfig: { - /** - * Agent host defaults to `::` dual stack. - * This is because the agent service is supposed to be public. - */ - agentHost: '::', - agentPort: 0, - /** - * Client host defaults to `localhost`. - * This will depend on the OS configuration. - * Usually it will be IPv4 `127.0.0.1` or IPv6 `::1`. - * This is because the client service is private most of the time. - */ - clientHost: 'localhost', - clientPort: 0, - /** - * If using dual stack `::`, then this forces only IPv6 bindings. - */ - ipv6Only: false, - - /** - * Agent service transport keep alive interval time. - * This the maxmum time between keep alive messages. - * This only has effect if `agentMaxIdleTimeout` is greater than 0. - * See the transport layer for further details. - */ - agentKeepAliveIntervalTime: 10_000, // 10 seconds - - /** - * Agent service transport max idle timeout. - * This is the maximum time that a connection can be idle. - * This also controls how long the transport layer will dial - * for a client connection. - * See the transport layer for further details. - */ - agentMaxIdleTimeout: 60_000, // 1 minute - - clientMaxIdleTimeout: 120, // 2 minutes - clientPingIntervalTime: 1_000, // 1 second - clientPingTimeoutTimeTime: 10_000, // 10 seconds - - /** - * Controls the stream parser buffer limit. - * This is the maximum number of bytes that the stream parser - * will buffer before rejecting the RPC call. - */ - clientParserBufferByteLimit: 1_000_000, // About 1MB - clientHandlerTimeoutTime: 60_000, // 1 minute - clientHandlerTimeoutGraceTime: 2_000, // 2 seconds - }, - nodeConnectionManagerConfig: { - connectionConnectTime: 2000, - connectionTimeoutTime: 60000, - initialClosestNodes: 3, - pingTimeoutTime: 2000, - connectionHolePunchTimeoutTime: 4000, - connectionHolePunchIntervalTime: 250, - }, - // This is not used by the `PolykeyAgent` which defaults to `{}` - network: { - mainnet: mainnet, - testnet: testnet, - }, + }, + /** + * This is not used by the `PolykeyAgent` which defaults to `{}` + * In the future this will be replaced by `mainnet.polykey.com` and `testnet.polykey.com`. + * Along with the domain we will have the root public key too. + * + * Information that is pre-configured during distribution: + * + * - Domain + * - Root public key + * + * Information that is discovered over DNS (Authenticated DNS is optional): + * + * - IP address + * - Port + * + * As long as the root public key is provided, it is sufficient to defeat poisoning + * the network. The root public key should also be changed often to reduce the impact + * of compromises. Finally the root public key can also be signed by a third party CA + * providing an extra level of confidence. However this is not required. + */ + network: { + mainnet: mainnet, + testnet: testnet, + }, + /** + * Default system configuration. + * These are not meant to be changed by the user. + * These constants are tuned for optimal operation by the developers. + */ + defaultSystem: { + /** + * Controls the stream parser buffer limit. + * This is the maximum number of bytes that the stream parser + * will buffer before rejecting the RPC call. + */ + rpcParserBufferByteLimit: 1_000_000, // About 1MB + rpcHandlerTimeoutTime: 60_000, // 1 minute + rpcHandlerTimeoutGraceTime: 2_000, // 2 seconds + + nodesInitialClosestNodes: 3, + + nodesConnectionConnectTime: 2000, + nodesConnectionTimeoutTime: 60000, + + nodesConnectionHolePunchTimeoutTime: 4000, + nodesConnectionHolePunchIntervalTime: 250, + + nodesPingTimeoutTime: 2000, + + clientTransportMaxIdleTimeoutTime: 120, // 2 minutes + clientTransportPingIntervalTime: 1_000, // 1 second + clientTransportPingTimeoutTime: 10_000, // 10 seconds + + /** + * Agent service transport keep alive interval time. + * This the maxmum time between keep alive messages. + * This only has effect if `agentMaxIdleTimeout` is greater than 0. + * See the transport layer for further details. + */ + agentConnectionKeepAliveIntervalTime: 10_000, // 10 seconds + /** + * Agent service transport max idle timeout. + * This is the maximum time that a connection can be idle. + * This also controls how long the transport layer will dial + * for a client connection. + * See the transport layer for further details. + */ + agentConnectionMaxIdleTimeoutTime: 60_000, // 1 minute + + + + + // Why are these done separately? + // Shouldn't we have a consistent time from NCM down to agent connection? + + // Transport layer is sort should be controlled separately? + + }, + /** + * Default user configuration. + * These are meant to be changed by the user. + * However the defaults here provide the average user experience. + */ + defaultsUser: { + nodePath: getDefaultNodePath(), + rootCertDuration: 31536000, + /** + * If using dual stack `::`, then this forces only IPv6 bindings. + */ + ipv6Only: false, + /** + * Agent host defaults to `::` dual stack. + * This is because the agent service is supposed to be public. + */ + agentServiceHost: '::', + agentServicePort: 0, + /** + * Client host defaults to `localhost`. + * This will depend on the OS configuration. + * Usually it will be IPv4 `127.0.0.1` or IPv6 `::1`. + * This is because the client service is private most of the time. + */ + clientServiceHost: 'localhost', + clientServicePort: 0, }, }; +type Config = typeof config; + export default config; + +export type { Config }; diff --git a/src/nodes/NodeConnectionManager.ts b/src/nodes/NodeConnectionManager.ts index 13c0f629b..bcb2fa52f 100644 --- a/src/nodes/NodeConnectionManager.ts +++ b/src/nodes/NodeConnectionManager.ts @@ -39,8 +39,6 @@ import * as utils from '../utils'; import { clientManifest as agentClientManifest } from '../agent/handlers/clientManifest'; import * as keysUtils from '../keys/utils'; -// TODO: check all locking and add cancellation for it. - type AgentClientManifest = typeof agentClientManifest; type ConnectionAndTimer = { diff --git a/src/schema/Schema.ts b/src/schema/Schema.ts index 0476c2e01..fefd3f8a4 100644 --- a/src/schema/Schema.ts +++ b/src/schema/Schema.ts @@ -61,7 +61,7 @@ class Schema { this.statePath = statePath; this.stateVersionPath = path.join( statePath, - config.defaults.stateVersionBase, + config.paths.stateVersionBase, ); this.stateVersion = stateVersion; this.fs = fs; diff --git a/src/vaults/VaultManager.ts b/src/vaults/VaultManager.ts index e98dc89c9..5abd69d8f 100644 --- a/src/vaults/VaultManager.ts +++ b/src/vaults/VaultManager.ts @@ -145,7 +145,7 @@ class VaultManager { }) { this.logger = logger; this.vaultsPath = vaultsPath; - this.efsPath = path.join(this.vaultsPath, config.defaults.efsBase); + this.efsPath = path.join(this.vaultsPath, config.paths.efsBase); this.db = db; this.acl = acl; this.keyRing = keyRing; diff --git a/tests/PolykeyAgent.test.ts b/tests/PolykeyAgent.test.ts index 9279b3159..9b0875422 100644 --- a/tests/PolykeyAgent.test.ts +++ b/tests/PolykeyAgent.test.ts @@ -66,34 +66,34 @@ describe('PolykeyAgent', () => { workers: 0, }); let nodePathContents = await fs.promises.readdir(nodePath); - expect(nodePathContents).toContain(config.defaults.statusBase); - expect(nodePathContents).toContain(config.defaults.stateBase); + expect(nodePathContents).toContain(config.paths.statusBase); + expect(nodePathContents).toContain(config.paths.stateBase); let stateContents = await fs.promises.readdir( - path.join(nodePath, config.defaults.stateBase), + path.join(nodePath, config.paths.stateBase), ); - expect(stateContents).toContain(config.defaults.keysBase); - expect(stateContents).toContain(config.defaults.dbBase); - expect(stateContents).toContain(config.defaults.vaultsBase); + expect(stateContents).toContain(config.paths.keysBase); + expect(stateContents).toContain(config.paths.dbBase); + expect(stateContents).toContain(config.paths.vaultsBase); await pkAgent.stop(); nodePathContents = await fs.promises.readdir(nodePath); - expect(nodePathContents).toContain(config.defaults.statusBase); - expect(nodePathContents).toContain(config.defaults.stateBase); + expect(nodePathContents).toContain(config.paths.statusBase); + expect(nodePathContents).toContain(config.paths.stateBase); stateContents = await fs.promises.readdir( - path.join(nodePath, config.defaults.stateBase), + path.join(nodePath, config.paths.stateBase), ); - expect(stateContents).toContain(config.defaults.keysBase); - expect(stateContents).toContain(config.defaults.dbBase); - expect(stateContents).toContain(config.defaults.vaultsBase); + expect(stateContents).toContain(config.paths.keysBase); + expect(stateContents).toContain(config.paths.dbBase); + expect(stateContents).toContain(config.paths.vaultsBase); await pkAgent.destroy(password); nodePathContents = await fs.promises.readdir(nodePath); // The status will be the only file left over expect(nodePathContents).toHaveLength(1); - expect(nodePathContents).toContain(config.defaults.statusBase); + expect(nodePathContents).toContain(config.paths.statusBase); }); test('start after stop', async () => { const nodePath = `${dataDir}/polykey`; - const statusPath = path.join(nodePath, config.defaults.statusBase); - const statusLockPath = path.join(nodePath, config.defaults.statusLockBase); + const statusPath = path.join(nodePath, config.paths.statusBase); + const statusLockPath = path.join(nodePath, config.paths.statusLockBase); const pkAgent = await PolykeyAgent.createPolykeyAgent({ password, nodePath, @@ -125,7 +125,7 @@ describe('PolykeyAgent', () => { }); test('schema state version is maintained after start and stop', async () => { const nodePath = path.join(dataDir, 'polykey'); - const statePath = path.join(nodePath, config.defaults.stateBase); + const statePath = path.join(nodePath, config.paths.stateBase); const schema = new Schema({ statePath, }); @@ -146,7 +146,7 @@ describe('PolykeyAgent', () => { }); test('cannot start during state version mismatch', async () => { const nodePath = path.join(dataDir, 'polykey'); - const statePath = path.join(nodePath, config.defaults.stateBase); + const statePath = path.join(nodePath, config.paths.stateBase); await fs.promises.mkdir(nodePath); let schema = await Schema.createSchema({ statePath, diff --git a/tests/PolykeyClient.test.ts b/tests/PolykeyClient.test.ts index 63ef7c6b7..a8a160706 100644 --- a/tests/PolykeyClient.test.ts +++ b/tests/PolykeyClient.test.ts @@ -42,7 +42,7 @@ describe('PolykeyClient', () => { }); test('preserving and destroying session state', async () => { const session = await Session.createSession({ - sessionTokenPath: path.join(nodePath, config.defaults.tokenBase), + sessionTokenPath: path.join(nodePath, config.paths.tokenBase), fs, logger, }); diff --git a/tests/bootstrap/utils.test.ts b/tests/bootstrap/utils.test.ts index b1936c6c2..e3a6b332a 100644 --- a/tests/bootstrap/utils.test.ts +++ b/tests/bootstrap/utils.test.ts @@ -39,14 +39,14 @@ describe('bootstrap/utils', () => { ).toBe(true); const nodePathContents = await fs.promises.readdir(nodePath); expect(nodePathContents.length > 0).toBe(true); - expect(nodePathContents).toContain(config.defaults.statusBase); - expect(nodePathContents).toContain(config.defaults.stateBase); + expect(nodePathContents).toContain(config.paths.statusBase); + expect(nodePathContents).toContain(config.paths.stateBase); const stateContents = await fs.promises.readdir( - path.join(nodePath, config.defaults.stateBase), + path.join(nodePath, config.paths.stateBase), ); - expect(stateContents).toContain(config.defaults.keysBase); - expect(stateContents).toContain(config.defaults.dbBase); - expect(stateContents).toContain(config.defaults.vaultsBase); + expect(stateContents).toContain(config.paths.keysBase); + expect(stateContents).toContain(config.paths.dbBase); + expect(stateContents).toContain(config.paths.vaultsBase); }); test('bootstraps existing but empty node path', async () => { const nodePath = path.join(dataDir, 'polykey'); @@ -65,14 +65,14 @@ describe('bootstrap/utils', () => { ).toBe(true); const nodePathContents = await fs.promises.readdir(nodePath); expect(nodePathContents.length > 0).toBe(true); - expect(nodePathContents).toContain(config.defaults.statusBase); - expect(nodePathContents).toContain(config.defaults.stateBase); + expect(nodePathContents).toContain(config.paths.statusBase); + expect(nodePathContents).toContain(config.paths.stateBase); const stateContents = await fs.promises.readdir( - path.join(nodePath, config.defaults.stateBase), + path.join(nodePath, config.paths.stateBase), ); - expect(stateContents).toContain(config.defaults.keysBase); - expect(stateContents).toContain(config.defaults.dbBase); - expect(stateContents).toContain(config.defaults.vaultsBase); + expect(stateContents).toContain(config.paths.keysBase); + expect(stateContents).toContain(config.paths.dbBase); + expect(stateContents).toContain(config.paths.vaultsBase); }); test('bootstrap fails if non-empty node path', async () => { // Normal file diff --git a/tests/client/handlers/agent.test.ts b/tests/client/handlers/agent.test.ts index a7192e624..b4e4ef6f6 100644 --- a/tests/client/handlers/agent.test.ts +++ b/tests/client/handlers/agent.test.ts @@ -299,8 +299,8 @@ describe('agentStop', () => { }); // Doing the test - const statusPath = path.join(nodePath, config.defaults.statusBase); - const statusLockPath = path.join(nodePath, config.defaults.statusLockBase); + const statusPath = path.join(nodePath, config.paths.statusBase); + const statusLockPath = path.join(nodePath, config.paths.statusLockBase); const status = new Status({ statusPath, statusLockPath, diff --git a/tests/status/Status.test.ts b/tests/status/Status.test.ts index 494d63825..0ed5b22be 100644 --- a/tests/status/Status.test.ts +++ b/tests/status/Status.test.ts @@ -26,8 +26,8 @@ describe('Status', () => { }); test('status readiness', async () => { const status = new Status({ - statusPath: path.join(dataDir, config.defaults.statusBase), - statusLockPath: path.join(dataDir, config.defaults.statusLockBase), + statusPath: path.join(dataDir, config.paths.statusBase), + statusLockPath: path.join(dataDir, config.paths.statusLockBase), fs: fs, logger: logger, }); @@ -48,8 +48,8 @@ describe('Status', () => { }); test('status transitions', async () => { const status = new Status({ - statusPath: path.join(dataDir, config.defaults.statusBase), - statusLockPath: path.join(dataDir, config.defaults.statusLockBase), + statusPath: path.join(dataDir, config.paths.statusBase), + statusLockPath: path.join(dataDir, config.paths.statusLockBase), fs: fs, logger: logger, }); @@ -86,16 +86,16 @@ describe('Status', () => { }); test('start with existing statusPath or statusLockPath', async () => { await fs.promises.writeFile( - path.join(dataDir, config.defaults.statusBase), + path.join(dataDir, config.paths.statusBase), 'hello world', ); await fs.promises.writeFile( - path.join(dataDir, config.defaults.statusLockBase), + path.join(dataDir, config.paths.statusLockBase), 'hello world', ); const status = new Status({ - statusPath: path.join(dataDir, config.defaults.statusBase), - statusLockPath: path.join(dataDir, config.defaults.statusLockBase), + statusPath: path.join(dataDir, config.paths.statusBase), + statusLockPath: path.join(dataDir, config.paths.statusLockBase), fs: fs, logger: logger, }); @@ -108,8 +108,8 @@ describe('Status', () => { }); test('readStatus on non-existent status', async () => { const status = new Status({ - statusPath: path.join(dataDir, config.defaults.statusBase), - statusLockPath: path.join(dataDir, config.defaults.statusLockBase), + statusPath: path.join(dataDir, config.paths.statusBase), + statusLockPath: path.join(dataDir, config.paths.statusLockBase), fs: fs, logger: logger, }); @@ -117,8 +117,8 @@ describe('Status', () => { }); test('updating live status', async () => { const status = new Status({ - statusPath: path.join(dataDir, config.defaults.statusBase), - statusLockPath: path.join(dataDir, config.defaults.statusLockBase), + statusPath: path.join(dataDir, config.paths.statusBase), + statusLockPath: path.join(dataDir, config.paths.statusLockBase), fs: fs, logger: logger, }); @@ -159,14 +159,14 @@ describe('Status', () => { }); test('singleton running status', async () => { const status1 = new Status({ - statusPath: path.join(dataDir, config.defaults.statusBase), - statusLockPath: path.join(dataDir, config.defaults.statusLockBase), + statusPath: path.join(dataDir, config.paths.statusBase), + statusLockPath: path.join(dataDir, config.paths.statusLockBase), fs: fs, logger: logger, }); const status2 = new Status({ - statusPath: path.join(dataDir, config.defaults.statusBase), - statusLockPath: path.join(dataDir, config.defaults.statusLockBase), + statusPath: path.join(dataDir, config.paths.statusBase), + statusLockPath: path.join(dataDir, config.paths.statusLockBase), fs: fs, logger: logger, }); @@ -182,8 +182,8 @@ describe('Status', () => { }); test('wait for transitions', async () => { const status = new Status({ - statusPath: path.join(dataDir, config.defaults.statusBase), - statusLockPath: path.join(dataDir, config.defaults.statusLockBase), + statusPath: path.join(dataDir, config.paths.statusBase), + statusLockPath: path.join(dataDir, config.paths.statusLockBase), fs: fs, logger: logger, }); @@ -213,8 +213,8 @@ describe('Status', () => { }); test('parse error when statusPath is corrupted', async () => { const status = new Status({ - statusPath: path.join(dataDir, config.defaults.statusBase), - statusLockPath: path.join(dataDir, config.defaults.statusLockBase), + statusPath: path.join(dataDir, config.paths.statusBase), + statusLockPath: path.join(dataDir, config.paths.statusLockBase), fs: fs, logger: logger, }); @@ -227,8 +227,8 @@ describe('Status', () => { }); test('status transitions are serialised', async () => { const status = new Status({ - statusPath: path.join(dataDir, config.defaults.statusBase), - statusLockPath: path.join(dataDir, config.defaults.statusLockBase), + statusPath: path.join(dataDir, config.paths.statusBase), + statusLockPath: path.join(dataDir, config.paths.statusLockBase), fs: fs, logger: logger, }); @@ -283,8 +283,8 @@ describe('Status', () => { }); test('wait for has at-least-once semantics', async () => { const status = new Status({ - statusPath: path.join(dataDir, config.defaults.statusBase), - statusLockPath: path.join(dataDir, config.defaults.statusLockBase), + statusPath: path.join(dataDir, config.paths.statusBase), + statusLockPath: path.join(dataDir, config.paths.statusLockBase), fs: fs, logger: logger, });