@opsimathically/nodenetproccalld 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.txt ADDED
@@ -0,0 +1 @@
1
+ Set your license text here.
package/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # nodenetproccalld
2
+
3
+ `nodenetproccalld` is a standalone TypeScript daemon that wraps:
4
+
5
+ - `@opsimathically/workerprocedurecall`
6
+ - `@opsimathically/networkprocedurecall`
7
+
8
+ It exposes a resilient mTLS TCP service with API-key authentication and privilege-based access control for remote function operations (`invoke`, `define`, `undefine`, constants, dependencies).
9
+
10
+ ## Features
11
+
12
+ - mTLS TCP daemon (`tls.createServer`) via `NetworkProcedureCall`
13
+ - API key auth callback with privilege mapping and optional TLS identity constraints
14
+ - JSON5 config files with comments/trailing commas support
15
+ - Startup validation for config shape, regex rules, and TLS PEM file presence
16
+ - Worker pool startup and worker event logging
17
+ - Graceful shutdown on `SIGINT` / `SIGTERM` / uncaught process errors
18
+ - Optional periodic runtime metrics logging
19
+
20
+ ## Config Files
21
+
22
+ Default paths:
23
+
24
+ - `./config/server.config.json5`
25
+ - `./config/api_keys.config.json5`
26
+
27
+ Sample files are included in `config/`.
28
+
29
+ ### Server Config (`server.config.json5`)
30
+
31
+ Defines:
32
+
33
+ - `information.server_name`
34
+ - `network.bind_addr` and `network.tcp_listen_port`
35
+ - `tls_mtls` file paths and TLS options
36
+ - `workerprocedurecall` worker count/options
37
+ - optional `abuse_controls`
38
+ - optional `observability` options
39
+
40
+ `tls_mtls.key_file`, `cert_file`, and `ca_file` are required and resolved relative to the server config file directory when not absolute paths.
41
+
42
+ ### API Keys Config (`api_keys.config.json5`)
43
+
44
+ Defines:
45
+
46
+ - `api_keys[]` entries with:
47
+ - `key_id`
48
+ - `api_key`
49
+ - `privileges`
50
+ - optional `enabled`
51
+ - optional `identity_constraints` regex patterns (`remote_address`, peer cert fields)
52
+
53
+ ## Running
54
+
55
+ 1. Build:
56
+
57
+ ```bash
58
+ npm run build
59
+ ```
60
+
61
+ 2. Start daemon with default config paths:
62
+
63
+ ```bash
64
+ npm run start
65
+ ```
66
+
67
+ 3. Generate TLS material for fresh installs (CA/server/client):
68
+
69
+ ```bash
70
+ node dist/index.js --generate-tls-material
71
+ ```
72
+
73
+ By default this writes:
74
+
75
+ - `./config/certs/ca.key.pem`
76
+ - `./config/certs/ca.cert.pem`
77
+ - `./config/certs/server.key.pem`
78
+ - `./config/certs/server.cert.pem`
79
+ - `./config/certs/client.key.pem`
80
+ - `./config/certs/client.cert.pem`
81
+
82
+ Useful options:
83
+
84
+ ```bash
85
+ node dist/index.js \
86
+ --generate-tls-material \
87
+ --tls-output-dir ./config/certs \
88
+ --tls-overwrite \
89
+ --tls-ca-cn my-local-ca \
90
+ --tls-server-cn localhost \
91
+ --tls-client-cn daemon-client \
92
+ --tls-valid-days 365
93
+ ```
94
+
95
+ 4. Start daemon with custom config paths:
96
+
97
+ ```bash
98
+ node dist/index.js \
99
+ --server-config /absolute/or/relative/server.config.json5 \
100
+ --api-keys-config /absolute/or/relative/api_keys.config.json5
101
+ ```
102
+
103
+ 5. CLI help:
104
+
105
+ ```bash
106
+ node dist/index.js --help
107
+ ```
108
+
109
+ Installed package binaries:
110
+
111
+ - `nodenetproccalld`
112
+
113
+ ## Packaging and Deployment
114
+
115
+ TypeScript daemons are usually deployed as built JavaScript plus a Node.js runtime.
116
+
117
+ ### Local package artifact
118
+
119
+ Build and create a deployable tarball:
120
+
121
+ ```bash
122
+ npm run build
123
+ npm pack
124
+ ```
125
+
126
+ This creates a file like:
127
+
128
+ - `opsimathically-nodenetproccalld-0.0.1.tgz`
129
+
130
+ ### Install on target host
131
+
132
+ Install Node.js LTS on target host, then install daemon package globally:
133
+
134
+ ```bash
135
+ npm install -g ./opsimathically-nodenetproccalld-0.0.1.tgz
136
+ ```
137
+
138
+ Then run:
139
+
140
+ ```bash
141
+ nodenetproccalld --help
142
+ ```
143
+
144
+ ### Typical service deployment (systemd)
145
+
146
+ Example `/etc/systemd/system/nodenetproccalld.service`:
147
+
148
+ ```ini
149
+ [Unit]
150
+ Description=nodenetproccalld
151
+ After=network.target
152
+
153
+ [Service]
154
+ Type=simple
155
+ User=nodenetprocd
156
+ WorkingDirectory=/opt/nodenetproccalld
157
+ ExecStart=/usr/bin/nodenetproccalld --server-config /opt/nodenetproccalld/config/server.config.json5 --api-keys-config /opt/nodenetproccalld/config/api_keys.config.json5
158
+ Restart=always
159
+ RestartSec=2
160
+ Environment=NODE_ENV=production
161
+
162
+ [Install]
163
+ WantedBy=multi-user.target
164
+ ```
165
+
166
+ Then:
167
+
168
+ ```bash
169
+ sudo systemctl daemon-reload
170
+ sudo systemctl enable nodenetproccalld
171
+ sudo systemctl start nodenetproccalld
172
+ sudo systemctl status nodenetproccalld
173
+ ```
174
+
175
+ ## Development
176
+
177
+ - Run tests:
178
+
179
+ ```bash
180
+ npm test
181
+ ```
182
+
183
+ - Run type checking:
184
+
185
+ ```bash
186
+ npm run typecheck
187
+ ```
188
+
189
+ - Run daemon directly from TypeScript sources:
190
+
191
+ ```bash
192
+ npm run start:daemon -- --server-config ./config/server.config.json5 --api-keys-config ./config/api_keys.config.json5
193
+ ```
194
+
195
+ ## mTLS Notes
196
+
197
+ - Use a private CA for server and client certs.
198
+ - Ensure client `servername` matches server cert SAN/CN (for local examples, `localhost`).
199
+ - Keep API key privileges minimal (`all_privileges` only for trusted operators).
@@ -0,0 +1,21 @@
1
+ {
2
+ // API keys and privilege grants for auth_callback.
3
+ api_keys: [
4
+ {
5
+ key_id: 'admin_key_1',
6
+ api_key: 'replace_me_with_random_secret',
7
+ privileges: ['all_privileges'],
8
+ enabled: true,
9
+ identity_constraints: {
10
+ // Example: accept loopback clients only.
11
+ remote_address_regex: '^(127\\.0\\.0\\.1|::1|::ffff:127\\.0\\.0\\.1)$'
12
+ }
13
+ },
14
+ {
15
+ key_id: 'invoke_only_key_1',
16
+ api_key: 'replace_me_with_second_secret',
17
+ privileges: ['invoke_functions'],
18
+ enabled: false
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,79 @@
1
+ {
2
+ // Friendly name that clients can use in their own config maps.
3
+ information: {
4
+ server_name: 'daemon_server_1'
5
+ },
6
+
7
+ // Bind target for tls.createServer.
8
+ network: {
9
+ bind_addr: '0.0.0.0',
10
+ tcp_listen_port: 6767
11
+ },
12
+
13
+ // PEM files are resolved relative to this config file unless absolute paths are used.
14
+ tls_mtls: {
15
+ key_file: './certs/server.key.pem',
16
+ cert_file: './certs/server.cert.pem',
17
+ ca_file: './certs/ca.cert.pem',
18
+ // crl_file: './certs/ca.crl.pem',
19
+ min_version: 'TLSv1.3',
20
+ handshake_timeout_ms: 5000,
21
+ request_timeout_ms: 15000,
22
+ max_frame_bytes: 1048576
23
+ },
24
+
25
+ workerprocedurecall: {
26
+ count: 4,
27
+ constructor_options: {
28
+ call_timeout_ms: 30000,
29
+ control_timeout_ms: 10000,
30
+ restart_on_failure: true,
31
+ max_restarts_per_worker: 6,
32
+ max_pending_calls_per_worker: 500
33
+ },
34
+ start_options: {
35
+ restart_base_delay_ms: 150,
36
+ restart_max_delay_ms: 5000,
37
+ restart_jitter_ms: 250
38
+ }
39
+ },
40
+
41
+ // Optional abuse controls from @opsimathically/networkprocedurecall.
42
+ abuse_controls: {
43
+ connection_controls: {
44
+ max_concurrent_sockets: 1024,
45
+ max_concurrent_handshakes: 256,
46
+ max_unauthenticated_sessions: 256,
47
+ per_ip_max_new_connections_per_window: 64,
48
+ tls_handshake_timeout_ms: 5000,
49
+ auth_message_timeout_ms: 5000
50
+ },
51
+ request_controls: {
52
+ max_in_flight_requests_per_connection: 128,
53
+ per_connection: {
54
+ enabled: true,
55
+ tokens_per_interval: 200,
56
+ interval_ms: 1000,
57
+ burst_tokens: 400
58
+ },
59
+ per_api_key: {
60
+ enabled: true,
61
+ tokens_per_interval: 1000,
62
+ interval_ms: 1000,
63
+ burst_tokens: 2000
64
+ },
65
+ per_ip: {
66
+ enabled: true,
67
+ tokens_per_interval: 500,
68
+ interval_ms: 1000,
69
+ burst_tokens: 1000
70
+ }
71
+ }
72
+ },
73
+
74
+ observability: {
75
+ enable_console_log: true,
76
+ log_worker_events: true,
77
+ metrics_log_interval_ms: 30000
78
+ }
79
+ }
@@ -0,0 +1,246 @@
1
+ import { privilege_name_t, networkprocedurecall_auth_callback_t, tls_min_version_t, networkprocedurecall_abuse_controls_t, networkprocedurecall_auth_success_t, networkprocedurecall_abuse_metrics_t } from '@opsimathically/networkprocedurecall';
2
+ import { workerprocedurecall_constructor_params_t, start_workers_params_t } from '@opsimathically/workerprocedurecall';
3
+
4
+ type daemon_tls_file_config_t = {
5
+ key_file: string;
6
+ cert_file: string;
7
+ ca_file: string;
8
+ crl_file?: string;
9
+ min_version?: tls_min_version_t;
10
+ cipher_suites?: string;
11
+ handshake_timeout_ms?: number;
12
+ request_timeout_ms?: number;
13
+ max_frame_bytes?: number;
14
+ };
15
+
16
+ type daemon_worker_config_t = {
17
+ count: number;
18
+ constructor_options?: workerprocedurecall_constructor_params_t;
19
+ start_options?: Omit<start_workers_params_t, 'count'>;
20
+ };
21
+
22
+ type daemon_observability_config_t = {
23
+ enable_console_log?: boolean;
24
+ log_worker_events?: boolean;
25
+ metrics_log_interval_ms?: number;
26
+ };
27
+
28
+ type daemon_server_config_file_t = {
29
+ information: {
30
+ server_name: string;
31
+ };
32
+ network: {
33
+ bind_addr: string;
34
+ tcp_listen_port: number;
35
+ };
36
+ tls_mtls: daemon_tls_file_config_t;
37
+ workerprocedurecall: daemon_worker_config_t;
38
+ abuse_controls?: networkprocedurecall_abuse_controls_t;
39
+ observability?: daemon_observability_config_t;
40
+ };
41
+
42
+ type daemon_api_key_identity_constraints_t = {
43
+ remote_address_regex?: string;
44
+ tls_peer_subject_regex?: string;
45
+ tls_peer_san_regex?: string;
46
+ tls_peer_fingerprint256_regex?: string;
47
+ tls_peer_serial_number_regex?: string;
48
+ };
49
+
50
+ type daemon_api_key_entry_t = {
51
+ key_id: string;
52
+ api_key: string;
53
+ privileges: privilege_name_t[];
54
+ enabled?: boolean;
55
+ identity_constraints?: daemon_api_key_identity_constraints_t;
56
+ };
57
+
58
+ type daemon_api_keys_config_file_t = {
59
+ api_keys: daemon_api_key_entry_t[];
60
+ };
61
+
62
+ type daemon_compiled_identity_constraints_t = {
63
+ remote_address_regex?: RegExp;
64
+ tls_peer_subject_regex?: RegExp;
65
+ tls_peer_san_regex?: RegExp;
66
+ tls_peer_fingerprint256_regex?: RegExp;
67
+ tls_peer_serial_number_regex?: RegExp;
68
+ };
69
+
70
+ type daemon_runtime_api_key_entry_t = Omit<
71
+ daemon_api_key_entry_t,
72
+ 'identity_constraints' | 'enabled'
73
+ > & {
74
+ enabled: boolean;
75
+ identity_constraints?: daemon_compiled_identity_constraints_t;
76
+ };
77
+
78
+ type daemon_resolved_tls_mtls_t = {
79
+ key_pem: string;
80
+ cert_pem: string;
81
+ ca_pem: string;
82
+ crl_pem?: string;
83
+ min_version?: tls_min_version_t;
84
+ cipher_suites?: string;
85
+ handshake_timeout_ms?: number;
86
+ request_timeout_ms?: number;
87
+ max_frame_bytes?: number;
88
+ };
89
+
90
+ type daemon_runtime_config_t = {
91
+ server_config: Omit<daemon_server_config_file_t, 'tls_mtls'> & {
92
+ tls_mtls: daemon_resolved_tls_mtls_t;
93
+ };
94
+ api_keys_config: {
95
+ api_keys: daemon_runtime_api_key_entry_t[];
96
+ };
97
+ };
98
+
99
+ type daemon_config_paths_t = {
100
+ server_config_path: string;
101
+ api_keys_config_path: string;
102
+ };
103
+
104
+ type daemon_tls_generation_options_t = {
105
+ enabled: boolean;
106
+ output_dir: string;
107
+ overwrite: boolean;
108
+ ca_common_name: string;
109
+ server_common_name: string;
110
+ client_common_name: string;
111
+ valid_days: number;
112
+ };
113
+
114
+ type daemon_generated_tls_material_t = {
115
+ output_dir: string;
116
+ ca_key_path: string;
117
+ ca_cert_path: string;
118
+ server_key_path: string;
119
+ server_cert_path: string;
120
+ client_key_path: string;
121
+ client_cert_path: string;
122
+ };
123
+
124
+ type daemon_cli_options_t = daemon_config_paths_t & {
125
+ help: boolean;
126
+ tls_generation: daemon_tls_generation_options_t;
127
+ };
128
+
129
+ type daemon_auth_callback_params_t = Parameters<networkprocedurecall_auth_callback_t>[0];
130
+
131
+ declare class ApiKeyAuthorizer {
132
+ private readonly api_key_entries;
133
+ constructor(params: {
134
+ api_key_entries: daemon_runtime_api_key_entry_t[];
135
+ });
136
+ authenticate(params: {
137
+ auth_callback_params: daemon_auth_callback_params_t;
138
+ }): Promise<'failed' | networkprocedurecall_auth_success_t>;
139
+ private matchesIdentityConstraints;
140
+ private matchOptionalValue;
141
+ }
142
+
143
+ declare class ConfigValidator {
144
+ validateServerConfig(params: {
145
+ server_config_raw: unknown;
146
+ }): daemon_server_config_file_t;
147
+ validateApiKeysConfig(params: {
148
+ api_keys_config_raw: unknown;
149
+ }): daemon_api_keys_config_file_t;
150
+ toRuntimeApiKeysConfig(params: {
151
+ api_keys_config: daemon_api_keys_config_file_t;
152
+ }): daemon_runtime_api_key_entry_t[];
153
+ private compileIdentityConstraints;
154
+ private assertUniqueApiKeys;
155
+ private assertUniqueKeyIds;
156
+ }
157
+
158
+ declare class ConfigFileLoader {
159
+ private readonly config_validator;
160
+ constructor(params?: {
161
+ config_validator?: ConfigValidator;
162
+ });
163
+ loadDaemonConfig(params: {
164
+ config_paths: daemon_config_paths_t;
165
+ }): daemon_runtime_config_t;
166
+ private readJson5File;
167
+ private resolveTlsMaterial;
168
+ private readPemFile;
169
+ }
170
+
171
+ declare class DaemonCli {
172
+ parseOptions(): daemon_cli_options_t;
173
+ printHelp(): void;
174
+ }
175
+
176
+ declare class DaemonProcess {
177
+ private readonly daemon;
178
+ private is_running;
179
+ private stop_in_progress;
180
+ private stop_resolve;
181
+ private readonly stop_waiter;
182
+ constructor(params: {
183
+ config_paths: daemon_config_paths_t;
184
+ });
185
+ run(): Promise<void>;
186
+ private attachProcessHandlers;
187
+ private detachProcessHandlers;
188
+ private readonly handleSignal;
189
+ private readonly handleUncaughtException;
190
+ private readonly handleUnhandledRejection;
191
+ private requestStop;
192
+ }
193
+
194
+ type daemon_lifecycle_state_t = 'stopped' | 'starting' | 'running' | 'stopping';
195
+ type daemon_runtime_snapshot_t = {
196
+ lifecycle_state: daemon_lifecycle_state_t;
197
+ server_name?: string;
198
+ bind_addr?: string;
199
+ tcp_listen_port?: number;
200
+ worker_health_states?: unknown;
201
+ abuse_metrics?: networkprocedurecall_abuse_metrics_t;
202
+ };
203
+ declare class NetworkProcedureCallDaemon {
204
+ private readonly config_paths;
205
+ private readonly config_file_loader;
206
+ private lifecycle_state;
207
+ private daemon_config;
208
+ private workerprocedurecall;
209
+ private networkprocedurecall;
210
+ private api_key_authorizer;
211
+ private worker_event_listener_id;
212
+ private metrics_log_interval_handle;
213
+ constructor(params: {
214
+ config_paths: daemon_config_paths_t;
215
+ config_file_loader?: ConfigFileLoader;
216
+ });
217
+ start(): Promise<void>;
218
+ stop(): Promise<void>;
219
+ getRuntimeSnapshot(): daemon_runtime_snapshot_t;
220
+ private createAuthCallback;
221
+ private buildServerStartParams;
222
+ private startMetricsLogging;
223
+ private clearMetricsLoggingInterval;
224
+ private stopBestEffort;
225
+ private requireDaemonConfig;
226
+ private requireApiKeyAuthorizer;
227
+ private logWorkerEvent;
228
+ private logMessage;
229
+ }
230
+
231
+ declare class TlsMaterialGenerator {
232
+ generateTlsMaterial(params: {
233
+ tls_generation_options: daemon_tls_generation_options_t;
234
+ }): daemon_generated_tls_material_t;
235
+ private assertOpenSslAvailable;
236
+ private buildTlsFileMap;
237
+ private assertTargetFilesAreWritable;
238
+ private generateCa;
239
+ private generateServerCertificate;
240
+ private generateClientCertificate;
241
+ private cleanupIntermediateFiles;
242
+ private runOpenSslCommand;
243
+ private getErrorMessage;
244
+ }
245
+
246
+ export { ApiKeyAuthorizer, ConfigFileLoader, ConfigValidator, DaemonCli, DaemonProcess, NetworkProcedureCallDaemon, TlsMaterialGenerator, type daemon_api_key_entry_t, type daemon_api_key_identity_constraints_t, type daemon_api_keys_config_file_t, type daemon_cli_options_t, type daemon_config_paths_t, type daemon_generated_tls_material_t, type daemon_observability_config_t, type daemon_runtime_api_key_entry_t, type daemon_runtime_config_t, type daemon_server_config_file_t, type daemon_tls_file_config_t, type daemon_tls_generation_options_t, type daemon_worker_config_t };