signalk-wakelogger 0.2.0-beta.0

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.
Files changed (83) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +201 -0
  3. package/README.md +49 -0
  4. package/SECURITY.md +7 -0
  5. package/assets/screenshots/plugin-config.png +0 -0
  6. package/assets/screenshots/plugin-status.png +0 -0
  7. package/assets/wakelogger.svg +6 -0
  8. package/dist/config/defaults.d.ts +18 -0
  9. package/dist/config/defaults.js +30 -0
  10. package/dist/config/defaults.js.map +1 -0
  11. package/dist/config/schema.d.ts +43 -0
  12. package/dist/config/schema.js +47 -0
  13. package/dist/config/schema.js.map +1 -0
  14. package/dist/index.d.ts +3 -0
  15. package/dist/index.js +338 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/outbox/database-outbox.d.ts +25 -0
  18. package/dist/outbox/database-outbox.js +186 -0
  19. package/dist/outbox/database-outbox.js.map +1 -0
  20. package/dist/outbox/database-types.d.ts +19 -0
  21. package/dist/outbox/database-types.js +3 -0
  22. package/dist/outbox/database-types.js.map +1 -0
  23. package/dist/outbox/factory.d.ts +12 -0
  24. package/dist/outbox/factory.js +98 -0
  25. package/dist/outbox/factory.js.map +1 -0
  26. package/dist/outbox/file-outbox.d.ts +27 -0
  27. package/dist/outbox/file-outbox.js +280 -0
  28. package/dist/outbox/file-outbox.js.map +1 -0
  29. package/dist/outbox/interface.d.ts +33 -0
  30. package/dist/outbox/interface.js +3 -0
  31. package/dist/outbox/interface.js.map +1 -0
  32. package/dist/pairing/association-client.d.ts +3 -0
  33. package/dist/pairing/association-client.js +31 -0
  34. package/dist/pairing/association-client.js.map +1 -0
  35. package/dist/pairing/credentials.d.ts +46 -0
  36. package/dist/pairing/credentials.js +168 -0
  37. package/dist/pairing/credentials.js.map +1 -0
  38. package/dist/pairing/pairing-client.d.ts +19 -0
  39. package/dist/pairing/pairing-client.js +102 -0
  40. package/dist/pairing/pairing-client.js.map +1 -0
  41. package/dist/signalk/normaliser.d.ts +11 -0
  42. package/dist/signalk/normaliser.js +119 -0
  43. package/dist/signalk/normaliser.js.map +1 -0
  44. package/dist/signalk/paths.d.ts +2 -0
  45. package/dist/signalk/paths.js +14 -0
  46. package/dist/signalk/paths.js.map +1 -0
  47. package/dist/signalk/subscriber.d.ts +2 -0
  48. package/dist/signalk/subscriber.js +20 -0
  49. package/dist/signalk/subscriber.js.map +1 -0
  50. package/dist/telemetry/profile-store.d.ts +7 -0
  51. package/dist/telemetry/profile-store.js +39 -0
  52. package/dist/telemetry/profile-store.js.map +1 -0
  53. package/dist/telemetry/profile.d.ts +32 -0
  54. package/dist/telemetry/profile.js +94 -0
  55. package/dist/telemetry/profile.js.map +1 -0
  56. package/dist/telemetry/sampler.d.ts +14 -0
  57. package/dist/telemetry/sampler.js +44 -0
  58. package/dist/telemetry/sampler.js.map +1 -0
  59. package/dist/telemetry/types.d.ts +73 -0
  60. package/dist/telemetry/types.js +3 -0
  61. package/dist/telemetry/types.js.map +1 -0
  62. package/dist/transport/adaptive-mode.d.ts +23 -0
  63. package/dist/transport/adaptive-mode.js +63 -0
  64. package/dist/transport/adaptive-mode.js.map +1 -0
  65. package/dist/transport/mqtt-client.d.ts +69 -0
  66. package/dist/transport/mqtt-client.js +365 -0
  67. package/dist/transport/mqtt-client.js.map +1 -0
  68. package/dist/transport/topics.d.ts +10 -0
  69. package/dist/transport/topics.js +13 -0
  70. package/dist/transport/topics.js.map +1 -0
  71. package/dist/trips/state-machine.d.ts +38 -0
  72. package/dist/trips/state-machine.js +118 -0
  73. package/dist/trips/state-machine.js.map +1 -0
  74. package/docs/architecture.md +17 -0
  75. package/docs/development.md +49 -0
  76. package/docs/implementation-plan.md +1943 -0
  77. package/docs/original-plan-gap-audit.md +47 -0
  78. package/docs/pairing.md +17 -0
  79. package/docs/release.md +52 -0
  80. package/docs/telemetry-protocol.md +50 -0
  81. package/docs/trip-detection.md +7 -0
  82. package/docs/validation.md +35 -0
  83. package/package.json +80 -0
@@ -0,0 +1,47 @@
1
+ # Original plan gap audit
2
+
3
+ This audit compares the implementation with the original 44-section implementation plan. “Implemented” means code and automated coverage exist; it does not substitute for hardware or fleet evidence.
4
+
5
+ | Original plan area | Status | Evidence or remaining gate |
6
+ |---|---|---|
7
+ | Purpose, scope and cloud/plugin boundary | Implemented | Live transport only; no archive upload, jurisdiction logic, cloud business rules or Signal K Track API provider. |
8
+ | TypeScript plugin lifecycle and offline startup | Implemented | Idempotent lifecycle and unpaired/offline integration test. |
9
+ | Signal K acquisition and normalization | Implemented for approved scope | Position/time, SOG, COG, true/magnetic heading, depth and apparent wind. Machinery, battery and tank data were removed by product decision. |
10
+ | Path sampling and remote profiles | Implemented | Versioned per-path periods for all three network modes; approved cloud profiles are persisted and acknowledged. |
11
+ | JSON protocol, batching and persistent sequence | Implemented | Protocol v1, bounded payloads, durable sequence assignment and cloud deduplication/ACK contract. |
12
+ | Durable outbox and retention | Implemented | Database API-preferred abstraction, native-free file fallback, crash-tail recovery, bounds, explicit sequence gaps and credential-bound backend selection that rejects partial-state sequence resets. Real released Database API compatibility remains an upstream availability gate. |
13
+ | MQTT 5, TLS, QoS 1 and ACL isolation | Implemented | Verified TLS, scoped topics, Last Will, reconnect jitter and app-level durable ACK. Disposable real-Mosquitto CI and the packaged Signal K Docker scenario verify TLS transport and crash recovery; release-environment ACL isolation remains a release-candidate gate. |
14
+ | Reconnect priority and constrained transport | Implemented | Retained current state, live sample priority, four bounded in-flight backlog batches, byte-rate limits and ACK/reconnect health modes. |
15
+ | Pairing and credential handling | Implemented | Short-lived single-use pairing, bounded retry, per-device credentials, protected storage, transactional replacement, explicit revocation status and an admin-only local forget action that preserves retired outboxes/sequences. |
16
+ | Tracking and trip evidence | Implemented within Wake Logger protocol | Persistent local evidence state; cloud remains authoritative. Evidence never creates a competing Signal K track service. |
17
+ | Status and cloud operations | Implemented | Queue, disk, age, drops, backend, mode, ACK latency, reconnect and profile revision metrics. |
18
+ | Unit, crash and integration testing | Implemented; hardware soak remains | Automated normalization, file/database outbox, corruption, lifecycle, real-broker transport, profile/adaptive, accelerated 24-hour restart/ACK recovery, three-day sampling and legacy-profile upgrade tests exist. A packaged Docker scenario adds real Signal K sample input, verified HTTPS/MQTT TLS, broker outage, `SIGKILL` and volume recovery. The extended outage and upgrade matrix must still run on target hardware. |
19
+ | Signal K CI and registry preparation | Implemented and passing | Normal Node 20/22 CI and the reusable Signal K matrix pass on Linux x64, Linux ARM64, ARMv7/Cerbo emulation, macOS and Windows. Signal K Server latest installs, enables and starts the packaged plugin on Node 22/24. The official registry harness preflight loads and activates it without configuration or unstubbed API use; install/tests/audit/changelog/screenshots meet the current 100-point criteria. |
20
+ | Bandwidth, CPU and memory validation | External evidence pending | Measure on the target Raspberry Pi/ARM installation under live, outage and replay workloads; do not infer hardware results from development hosts. |
21
+ | Raspberry Pi/ARM and real-vessel test | External evidence pending | Required before first-vessel and stable-production sign-off, but not for an npm beta. The beta is the field-validation channel. Hardware targets are intentionally not embedded in this public repository. |
22
+ | npm/AppStore publication | Prepared, not executed | Physical hardware is not a beta prerequisite. The beta-only OIDC workflow uses an exact version tag, main ancestry, Node 24/npm 12 and the `beta` dist-tag. The available package name requires a documented one-time 2FA bootstrap before OIDC can be configured. No stable release is automated until its acceptance gate is agreed. |
23
+
24
+ ## npm beta gate
25
+
26
+ - Normal CI and the Signal K reusable plugin matrix pass on the exact `main` commit.
27
+ - The packaged Signal K/TLS/MQTT Docker recovery scenario passes on that commit.
28
+ - The installed-package audit, package contents, metadata, changelog and public-repository scan pass.
29
+ - The release uses a prerelease version and npm's `beta` dist-tag.
30
+
31
+ Raspberry Pi, real-vessel, soak and target-hardware resource measurements are explicitly outside this publication gate. They remain product-readiness evidence and can be collected from the published beta.
32
+
33
+ ## First-vessel blockers
34
+
35
+ - Repeat the green normal and reusable Signal K matrices against the exact release-candidate commit after any further code change.
36
+ - Repeat the packaged TLS broker/ingestion scenario on the exact release-candidate commit and release environment.
37
+ - Run restart, 30-minute outage and constrained-link scenarios on the intended Signal K host.
38
+ - Record bandwidth, CPU, memory and queue disk results on that host.
39
+ - Verify the documented install, upgrade and full-data-removal/re-pair semantics on the target host; partial credential-only restoration is rejected by design.
40
+
41
+ ## Stable public-release blockers
42
+
43
+ - Complete multi-day poor-connectivity and upgrade soak evidence.
44
+ - Complete real-vessel and ARM evidence.
45
+ - Confirm the authoritative Signal K registry score and rendered screenshots after the first npm beta becomes discoverable in the actual AppStore.
46
+ - Test credential revocation and broker ACL isolation in the release environment.
47
+ - Agree the stable-release gate; the current workflow deliberately publishes beta tags only.
@@ -0,0 +1,17 @@
1
+ # Pairing and credentials
2
+
3
+ The plugin POSTs `{ "code": "...", "name": "Signal K ..." }` to the configured HTTPS pairing endpoint. A valid response provides a nested device and credentials object containing device ID, required MQTT client ID, username, one-time password, host, port, TLS requirement, telemetry profile, association-status URL and device-scoped association token. Non-production endpoint overrides belong only in local Signal K configuration and must never be committed.
4
+
5
+ The plugin accepts only HTTPS endpoints, bounded identifiers, DNS hostnames and valid TCP ports. The service should issue codes that are hashed at rest, single-use, account-authorized and expire after ten minutes. Each broker identity must be restricted to publishing its telemetry/state/status/events topics and subscribing only to its acknowledgement topic.
6
+
7
+ Credentials are stored beneath `app.getDataDirPath()` with directory mode `0700` and file mode `0600`. Rotation should preserve device ID and sequence space. Device replacement creates a new device identity; Wake Logger keeps the current device active until provisioning the replacement succeeds, then revokes and disconnects the old broker client. There is no insecure TLS fallback and production credentials must never be placed in source, plugin configuration, screenshots or logs.
8
+
9
+ Pairing retries only temporary network, timeout, rate-limit and server failures. It makes at most six attempts with 2, 4, 8, 16 and 30 second delays. An HTTP 400 means the code is invalid or expired and is not retried. If replacement pairing exhausts its retries, the existing credential remains active. Signal K status shows the attempt and delay so the operator can distinguish `Retry pairing` from a rejected code.
10
+
11
+ The credential record keeps only a SHA-256 fingerprint of the last consumed pairing code. Restarting with that same single-use code does not retry pairing. Supplying a different code explicitly pairs a replacement device and atomically replaces the credential record; the raw pairing code is never copied into credential storage. Outboxes are device-scoped: a replacement starts its own sequence space while the retired device's unacknowledged files remain preserved locally and are never published under the new identity.
12
+
13
+ Newly provisioned credentials can query their own association state over authenticated outbound HTTPS. After broker authentication fails, the plugin checks that endpoint. A confirmed revocation changes status to `Device revoked — enter a new pairing code` and stops local sampling/transmission; unavailable or inconclusive status checks leave the normal authentication failure intact. Older credentials without this status token remain compatible but cannot distinguish revocation until they pair once again.
14
+
15
+ Signal K administrators may invoke `POST /plugins/signalk-wakelogger/forget-credentials`. The action closes active transport and removes only `credentials.json`. It records a non-secret retirement tombstone so the consumed code is not replayed, and preserves the installation identity, device-scoped outbox directories, sequence state and diagnostics. The operator must enter a different, fresh pairing code. This local action does not revoke cloud broker access by itself; use Wake Logger's vessel or site-admin revoke workflow when retiring the association.
16
+
17
+ After the first outbox opens, the credential record is atomically bound to its selected `file` or `database` backend. If credentials survive but the backend marker is later missing or disagrees, startup fails visibly instead of recreating sequence 1 for the same cloud device. A normal upgrade or reinstall with the plugin data directory preserved retains credentials, binding and sequence state. Removing the entire data directory also removes credentials and requires pairing a newly provisioned device identity; selectively restoring credentials without their bound outbox state is intentionally rejected.
@@ -0,0 +1,52 @@
1
+ # Release process
2
+
3
+ The repository is beta-only until the stable acceptance gate is agreed. A `prepublishOnly` guard rejects any version that does not end in `-beta.<number>` and any publish without the explicit `--tag beta` option. The GitHub workflow accepts only an exact `v<package-version>` tag and requires the tagged commit to be reachable from `main`. There is no stable or `latest` publishing path.
4
+
5
+ Physical Raspberry Pi, target-hardware and real-vessel testing are not prerequisites for an npm beta. Beta publication is the supported way to collect that field evidence. Those results remain required before declaring the plugin first-vessel or stable-production ready.
6
+
7
+ ## npm beta gate
8
+
9
+ An npm beta release candidate must:
10
+
11
+ - be merged to `main` with normal CI and Signal K reusable plugin CI passing;
12
+ - pass `npm run check` and `npm audit --omit=dev --audit-level=high`;
13
+ - pass the packaged `npm run test:docker` Signal K/TLS/MQTT recovery scenario;
14
+ - pass `npm pack --dry-run`, with the intended files, beta version and Signal K metadata present; and
15
+ - have an updated changelog and no production or non-production Wake Logger endpoints, credentials or environment details in the public package.
16
+
17
+ Physical hardware results do not affect this gate. Record them separately under the first-vessel and stable-release gates.
18
+
19
+ ## One-time npm bootstrap
20
+
21
+ The unscoped `signalk-wakelogger` package does not yet exist in npm. npm trusted-publisher configuration requires an existing package, so the first beta must be bootstrapped deliberately by the package owner:
22
+
23
+ 1. Merge an approved release-candidate pull request to `main` after the npm beta gate above passes.
24
+ 2. From a clean checkout of that exact `main` commit, use Node.js 24 and npm 12.0.2.
25
+ 3. Run `npm ci --ignore-scripts`, `npm run check`, `npm audit --omit=dev --audit-level=high`, `npm run test:docker`, and `npm pack --dry-run`; inspect the file list.
26
+ 4. With the npm account protected by 2FA, run `npm publish --access public --tag beta`. The prepublish guard verifies the beta version/tag and repeats the complete check automatically; a bare `npm publish` is rejected.
27
+ 5. Confirm that npm shows the version under `beta`, not `latest`.
28
+
29
+ This is the only manual package publication. Do not create the Git tag before the package bootstrap succeeds.
30
+
31
+ ## Configure trusted publishing
32
+
33
+ After the package exists, configure its npm trusted publisher with:
34
+
35
+ - provider: GitHub Actions
36
+ - repository: `nerdynigel/signalk-wakelogger`
37
+ - workflow filename: `publish.yml`
38
+ - allowed action: `npm publish`
39
+ - environment: `npm`
40
+
41
+ Create the GitHub `npm` environment and require a maintainer reviewer before deployment. The workflow uses a GitHub-hosted runner with `id-token: write`, Node.js 24 and npm 12.0.2. npm generates provenance automatically for trusted publication from this public repository. After the first successful OIDC beta, disallow traditional publish tokens in npm package settings.
42
+
43
+ After configuring trust, create and push the exact bootstrap-version tag. The workflow detects that the manually bootstrapped version already exists, does not republish it, and creates the matching GitHub prerelease. This also makes a safe workflow retry idempotent after npm succeeds but GitHub release creation fails.
44
+
45
+ ## Subsequent betas
46
+
47
+ 1. Change `package.json` and the lockfile to the next beta version and update `CHANGELOG.md`.
48
+ 2. Merge only a release candidate that passes the npm beta gate to `main`.
49
+ 3. Create and push the exact matching tag, such as `v0.2.0-beta.1`.
50
+ 4. Verify the Publish workflow, npm provenance, `beta` dist-tag, GitHub prerelease notes and Signal K registry result.
51
+
52
+ Never reuse or move a published tag. A stable workflow must be introduced separately and reviewed against an explicitly agreed stable-release gate.
@@ -0,0 +1,50 @@
1
+ # Telemetry protocol v1
2
+
3
+ All topics are beneath `wakelogger/v1/devices/<deviceId>`:
4
+
5
+ | Suffix | Direction | Retained | Purpose |
6
+ |---|---|---:|---|
7
+ | `telemetry` | device → cloud | no | Ordered sample batches |
8
+ | `state` | device → cloud | yes | Latest sample for reconnect priority |
9
+ | `status` | device → cloud | yes | Online/offline state, Last Will and bounded plugin/outbox diagnostics |
10
+ | `events` | device → cloud | no | Advisory trip evidence |
11
+ | `ack` | cloud → device | yes | Highest contiguously committed sequence, restored on reconnect |
12
+ | `profile` | cloud → device | yes | Approved, versioned sampling and transport profile |
13
+ | `profile-ack` | device → cloud | yes | Applied/rejected profile revision result |
14
+
15
+ Telemetry is JSON and bounded to 64 KiB. A batch has `v`, `deviceId`, optional `droppedThrough`, and up to 60 `samples`. `droppedThrough` explicitly closes a sequence gap caused by local retention or corrupt-tail recovery so the cloud can advance its contiguous cursor while recording data loss. Each sample contains:
16
+
17
+ ```json
18
+ {
19
+ "v": 1,
20
+ "deviceId": "dev_xxx",
21
+ "sequence": 184291,
22
+ "capturedAt": 1788143912000,
23
+ "receivedAt": 1788143912050,
24
+ "trackingSessionId": "optional-uuid",
25
+ "values": {
26
+ "lat": -27.41238,
27
+ "lon": 153.18291,
28
+ "sog_kn": 6.42,
29
+ "cog_deg": 241.3,
30
+ "heading_deg": 238.8,
31
+ "heading_reference": "true",
32
+ "depth_m": 12.4,
33
+ "aws_kn": 15.2,
34
+ "awa_deg": 315
35
+ },
36
+ "quality": {
37
+ "timestamp": "source"
38
+ }
39
+ }
40
+ ```
41
+
42
+ Times are UTC Unix milliseconds. Latitude/longitude are decimal degrees; speeds are knots; angles are degrees in `[0,360)`; depth is metres. Apparent wind angle follows Signal K's starboard-positive convention after normalization. Heading uses true heading when present and explicitly marks magnetic fallback.
43
+
44
+ An acknowledgement is `{"v":1,"deviceId":"dev_xxx","ackSequence":184291,"acknowledgedAt":1788143912200}`. It means every sequence through that value was durably accepted or intentionally rejected under a server policy. Older backlog may arrive after retained current state; it must still be inserted by capture time. Current state changes only when `capturedAt` is newer.
45
+
46
+ Trip evidence is also embedded in its source sample for durable recovery. The event topic is a convenience notification and consumers must deduplicate by `deviceId + sequence`.
47
+
48
+ The retained status document includes `v`, `state` and `at`. A running plugin also reports `pluginVersion`, `queueMessageCount`, `queueDiskBytes`, optional `queueOldestCapturedAt`, `queueDroppedCount`, `acknowledgedSequence`, `currentSequence` and optional `trackingState`. These fields contain operational counters only—never credentials or telemetry values—and allow Wake Logger administrators to identify vessel-side backlog and loss.
49
+
50
+ Status also reports `storageBackend`, `networkMode`, `modeReason`, `profileId`, `profileRevision`, `lastAcknowledgedAt`, `acknowledgementLatencyMs`, `reconnectCount` and `publishedBytes`. Profiles contain exactly the supported fields (`position`, `sog`, `cog`, `heading`, `depth`, `apparentWind`) with enabled flags and millisecond periods for `NORMAL`, `CONSTRAINED` and `OFFLINE`, plus bounded batching and replay byte rates. Position cannot be disabled. A profile acknowledgement reports `profileId`, `revision`, `status`, `at`, and a bounded error code when rejected.
@@ -0,0 +1,7 @@
1
+ # Trip detection
2
+
3
+ The plugin emits evidence; Wake Logger remains authoritative for provisional and final trip boundaries.
4
+
5
+ The state machine is `STOPPED → START_CANDIDATE → MOVING → STOP_CANDIDATE → STOPPED`. A start candidate begins when SOG exceeds 1.5 kn or the vessel moves more than 200 m from its stationary cluster. Two minutes of credible movement confirms it and backdates the effective time. A stop candidate begins below 0.5 kn; remaining within 75 m for 15 minutes confirms it and backdates the effective end.
6
+
7
+ The stationary origin uses a slowly adjusted centroid only while points remain inside the 75 m berth cluster. This absorbs normal GPS drift without dragging the origin along a slow marina departure. Engine data is neither subscribed nor required, so engine-off sailing remains valid. Connectivity and sensor interruption never end a tracking session. Evidence includes effective time, speed, distance and dwell duration. State is persisted across Signal K and vessel restarts.
@@ -0,0 +1,35 @@
1
+ # Validation evidence
2
+
3
+ Automated checks cover lifecycle startup without configuration/network, supported-path normalization, managed profiles, adaptive modes, MQTT scheduling, application acknowledgements, file/database outbox restart continuity, corrupt file-tail repair, retention gaps and sequence-safe backend selection. Pairing tests additionally cover bounded transient retry, immediate invalid-code rejection, device-scoped revocation checks and local credential forgetting without deletion or reuse of retired outbox/sequence state. Accelerated release scenarios cover a 24-hour file-outbox outage with restart and partial application ACK, a three-day NORMAL/OFFLINE/CONSTRAINED sampling timeline, and conversion of the earlier telemetry-profile shape.
4
+
5
+ A disposable Mosquitto 2 CI job exercises the real MQTT.js network path independently of Wake Logger infrastructure. It verifies MQTT 5/QoS 1 publication, retained current-state priority, ordered backlog delivery and removal only after an application ACK delivered through the broker.
6
+
7
+ The Docker end-to-end job builds the exact local npm tarball, installs it into a pinned official Signal K image and consumes Signal K's synthesized NMEA 2000 stream. It asserts the resulting telemetry contains position, SOG, COG, true heading, depth and both apparent-wind values. A runtime-generated two-day CA secures both the mock HTTPS pairing service and TLS-only MQTT broker; the plugin uses normal hostname and certificate verification. The mock service syncs each received event to disk before publishing its application ACK. The scenario then stops the broker, kills Signal K with `SIGKILL`, preserves the Signal K data volume, and verifies recovered sequences, current-state-before-backlog ordering and an empty queue after ACK. No test-only branch exists in plugin runtime code. Scoped broker ACL enforcement remains covered in the private cloud integration suite and must be repeated against the release environment.
8
+
9
+ On 2026-09-01 the final local x64 Docker scenario acknowledged sequence 3 before the outage and sequence 17 after restart, recovering 14 samples produced during the outage. It observed current state before backlog and the final zero-message queue status. A one-shot post-recovery snapshot reported about 276 MiB for the complete Signal K container, 45 MiB for the durable mock service and 3.2 MiB for Mosquitto. These transient development-container values are smoke evidence only: they include the full server, are not a soak average, and do not replace target ARM measurements. Every run prints its own CPU, memory, network and block-I/O snapshot.
10
+
11
+ On 2026-09-01 the rewritten pull-request commit passed normal CI on Node.js 20/22 and the Signal K reusable matrix on Linux x64, Linux ARM64, macOS and Windows with Node.js 22/24, plus ARMv7/Cerbo emulation on Node.js 20. The integration matrix installed, enabled and started the packaged plugin in Signal K Server latest on Node.js 22/24. This is portable package evidence, not a substitute for resource measurements on the target vessel hardware.
12
+
13
+ The 2026-09-01 official Signal K plugin-registry harness preflight reported `loads`, `activates`, `activatesWithoutConfig` and `hasSchema` as true, with no provider requirement, error messages or unstubbed API accesses. Together with the lifecycle-script-free install, passing source tests, clean runtime audit, packaged changelog and screenshots, the unpublished package satisfies the current 100-point criteria in preflight. Only a published npm version can receive an authoritative registry score and AppStore listing.
14
+
15
+ An npm 11 release dry-run executed the real `prepublishOnly` guard and complete check, then produced an 80-file, 69.2 kB beta tarball with public access and the `beta` dist-tag. A separate negative invocation confirmed the guard rejects `latest`. No package, tag or release was published.
16
+
17
+ `npm run measure:protocol` encodes a synthetic 24-hour, 1 Hz stream with every supported optional field. It counts normal online application payloads: one retained state and one single-sample telemetry batch each second, plus a representative status document every ten seconds. MQTT/TCP/TLS overhead and storage/Signal K resource use are intentionally excluded. The checked target is below 1 KiB/s before transport overhead.
18
+
19
+ The benchmark is reproducible, but development-host CPU time and RSS are not vessel hardware evidence. Record release-candidate results here only after running the packaged plugin on the target Signal K ARM host:
20
+
21
+ The 2026-08-31 Node.js 20 x64 protocol run encoded 86,400 seconds in 1.45 seconds and measured 340 bytes per full sample and 780 application bytes/second for normal online traffic. A checksummed file record is approximately 376 bytes, or 31.0 MiB for a 24-hour 1 Hz worst-case queue. Process RSS was about 32 MiB during this encoding-only run. These values pass the pre-transport bandwidth target but are not full-plugin or ARM measurements.
22
+
23
+ A 2026-09-01 Node.js 22 x64 accelerated production file-outbox run wrote and synced 300 full samples in 1.34 seconds. It measured 339.84 disk bytes per sample, a 28.0 MiB/day projection, 970 ms user CPU, 147 ms system CPU and 37.29 MiB peak process RSS. This development-host stress evidence confirms the queue-size estimate and provides a regression baseline; it is neither real-time operation nor ARM evidence.
24
+
25
+ | Evidence | Development gate | First-vessel gate |
26
+ |---|---|---|
27
+ | Protocol application bytes | Automated 24-hour encoding model | Capture actual MQTT/TLS bytes during normal and replay traffic |
28
+ | CPU and memory | CI regression/no leak checks | Multi-day process metrics on target ARM host |
29
+ | Outbox disk and writes | Bounded/corruption unit tests | 24-hour offline queue growth and disk-write measurement |
30
+ | Reconnect/replay | Deterministic scheduler plus real Signal K/TLS/broker crash recovery | Poor-link and real-time 30-minute/24-hour target scenarios |
31
+ | Install/upgrade | npm package dry run, lifecycle CI and packaged Docker install/restart | Version upgrade, reboot and uninstall/reinstall on target |
32
+
33
+ Do not mark ARM or real-vessel rows complete using x64 development-host results.
34
+
35
+ For repeatable target-host collection, install the published beta (or build the exact release commit before publication) and run `npm run measure:outbox -- 3600` as an initial accelerated synced-write comparison, then run the installed plugin in real time for the required outage. Archive the JSON output with the commit, device model, storage medium and Signal K/Node versions. The stress tool uses production file-outbox code but does not exercise Signal K subscriptions, MQTT/TLS, scheduling or Database API storage, so it cannot by itself satisfy the first-vessel hardware gate. This evidence is not required to publish an npm beta.
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "signalk-wakelogger",
3
+ "version": "0.2.0-beta.0",
4
+ "description": "Resilient live Signal K telemetry transport for Wake Logger",
5
+ "license": "Apache-2.0",
6
+ "author": "Nigel George",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/nerdynigel/signalk-wakelogger.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/nerdynigel/signalk-wakelogger/issues"
13
+ },
14
+ "homepage": "https://github.com/nerdynigel/signalk-wakelogger#readme",
15
+ "main": "dist/index.js",
16
+ "types": "dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "require": "./dist/index.js",
21
+ "default": "./dist/index.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "assets",
27
+ "docs",
28
+ "README.md",
29
+ "CHANGELOG.md",
30
+ "SECURITY.md",
31
+ "LICENSE"
32
+ ],
33
+ "engines": {
34
+ "node": ">=20"
35
+ },
36
+ "keywords": [
37
+ "signalk-node-server-plugin",
38
+ "signalk-category-cloud",
39
+ "signalk",
40
+ "marine",
41
+ "telemetry",
42
+ "mqtt"
43
+ ],
44
+ "scripts": {
45
+ "build": "tsc -p tsconfig.build.json",
46
+ "typecheck": "tsc --noEmit",
47
+ "test": "vitest run",
48
+ "test:integration": "vitest run test/integration",
49
+ "test:broker": "vitest run test/integration/mqtt-broker.test.ts",
50
+ "test:docker": "node scripts/test-signalk-docker.mjs",
51
+ "test:watch": "vitest",
52
+ "lint": "eslint src test scripts --max-warnings=0",
53
+ "check:public": "node scripts/check-public-repo.mjs",
54
+ "measure:protocol": "node scripts/measure-protocol.mjs",
55
+ "measure:outbox": "npm run build && node scripts/measure-outbox.mjs",
56
+ "prepublishOnly": "node scripts/verify-release.mjs && npm run check",
57
+ "check": "npm run check:public && npm run lint && npm run typecheck && npm test && npm run build"
58
+ },
59
+ "dependencies": {
60
+ "mqtt": "^5.15.2"
61
+ },
62
+ "devDependencies": {
63
+ "@signalk/server-api": "^2.31.1",
64
+ "@types/node": "^20.17.30",
65
+ "@typescript-eslint/eslint-plugin": "^8.28.0",
66
+ "@typescript-eslint/parser": "^8.28.0",
67
+ "eslint": "^9.23.0",
68
+ "typescript": "^5.8.2",
69
+ "vitest": "^2.1.9"
70
+ },
71
+ "signalk": {
72
+ "displayName": "Wake Logger",
73
+ "appIcon": "./assets/wakelogger.svg",
74
+ "screenshots": [
75
+ "./assets/screenshots/plugin-status.png",
76
+ "./assets/screenshots/plugin-config.png"
77
+ ]
78
+ },
79
+ "signalk-plugin-enabled-by-default": false
80
+ }