-
Notifications
You must be signed in to change notification settings - Fork 71
Data & protocol documentation and management roadmap #671
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
Great initiative. TypeBox sounds better than Zod for our user case, plus asyncAPI is well established in the IoT world. I really like this. My perspective and focus is client-side and it's tooling. An important aspect this PR should try and improve is the delta stream data discovery and validation from the a client side. TypeBox has a very efficient TypeBox type native compiler that generates validation rules for validation at runtime. Ideally, clients would be able to reuse Signal K server's TypeBox types. It would enable clients to load types once on app startup and compile types rules, This gives TypeScript types IDE support and object validation on delta updates, v2 API and if you want, Angular and probably React forms validation. Wouldn't this warrant keeping @signalk/server-api alive to host both delta and v2 api types for easy distribution? Or maybe there is a better way? The second point is enabling type discovery and validation. For this we need a discriminator field for each object so that we don't have to iterate and compare all types all the time. Maybe the current the delta path property is suitable? I'm asking because it might impact how we design types, while keeping the current structure intact. I'be will to participate in testing from a client side and collaboration on a client SDK of some sort... |
|
Thank you for your great feedback. I have tried to cover your topics and changed the document. |
|
Great! Thank you |
tkurki
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's my first review round. Trying to wrap my head around this in detail it becomes pretty quickly obvious that while I think the direction is good there's LOT of detail here and there's no going all in to this one.
I think we should find a way to evolve server-api instead of trying to replace it wholesale.
One fundamental worry that I have is that I don't think it is good idea to have a type per path, but find a way tohave types that cover well known paths as well as dynamic paths. A way to tell that "this is a value for path x.y.z that is numeric/timestamp/string/position value" without having type for each path.
|
Thanks for the detailed review, Teppo. Your feedback fundamentally reshaped the proposal. In particular: The type-per-path concern was the crux. You're absolutely right that Evolve, don't replace. The proposal now builds on Dynamic data model preserved. Custom paths and runtime metadata injection remain first-class. v1 schema and HTTP API stay for paths without v2 coverage. I also corrected my misunderstanding of the existing packages — The stages are now more incremental. Each provides value independently, no "all in" required. Let me know if this direction works better. |
|
I hear you Teppo. To Dirk's point, none of this breaks the way SK works today but it is a significant addition. We are adding modern type tooling that can be put to work - so we can discover and validate objects and API contracts. Well potentially any object, where it makes sense. To me, it's object that are the main area for improvement. The problem I want to solve is: what kind of object does this path send? Once I know, is it valide? In KIP code: what does/should a SK AIS object contain? How can I get the IDE to tell me. |
|
Thanks David, that's a really helpful simplification. You're right — primitives don't need TypeBox. Clients already have So the proposal now focuses TypeBox on objects only: Position, Attitude, AIS targets, Notifications, etc. That's where clients actually need to know the shape and validate the structure. Primitives just get metadata with units/description — no TypeBox overhead. |
If we bind path to type, like using a meta field, or a fixed path rule...whatever, we can query both ways:
To keep using the old way, you just ignore the meta type. Or you make sure you don't have that property. |
|
@dirkwa maybe trying a few objects is a good way to start. I like the metadata approach to tag TypeBox types but like you said, it's a bit of an overlap with meta units. That said, object are bound to have units as properties. Not |
|
Good points, David. You're right about bidirectional querying — if we bind paths to types, clients can ask both "what does this path emit?" and "give me all paths that emit Position." That's useful for discovery. Added to the proposal. On units: I think there's a natural split. Primitives have units in path metadata. Objects have units on their individual properties in the schema. No real overlap. Agree that starting with a few objects is the way to go. |
Proposal: Evolving Signal K Schema Management with TypeBox
Executive Summary
This proposal suggests an incremental evolution of Signal K's schema management, building on existing packages and infrastructure. The goal is to consolidate documentation, reduce maintenance burden, and improve the developer experience — while preserving Signal K's dynamic data model.
The approach: evolve what exists, don't replace it wholesale.
Current State
What We Have Today
Signal K has several interconnected pieces for schema and type management:
Specification Repo (
github.com/SignalK/specification)@signalk/signalk-schemaPackagekeyswithmetadata.json)@signalk/server-apiPackageServer OpenAPI (hand-written JSON files)
/admin/openapiServer Documentation
What's Working
@signalk/server-apiprovides useful TypeScript types used across the ecosystem@signalk/signalk-schemaprovides validation for convertersPain Points
What Must Be Preserved
Signal K's dynamic data model is a core strength:
Proposed Direction
Guiding Principles
What This Proposal IS
What This Proposal is NOT
Why TypeBox
The Problem with JSON Schema Maintenance
JSON Schema is powerful but tedious to maintain by hand. Adding a new well-known path requires editing verbose JSON files, and there's no IDE support or type inference.
TypeBox Advantages
TypeBox schemas ARE JSON Schema — they produce it natively, not through conversion. This means:
Static<typeof Schema>Performance
TypeBox is significantly faster than alternatives:
Benchmarks from moltar/typescript-runtime-type-benchmarks
JSON Schema Feature Support
TypeBox supports features that alternatives lack:
patternProperties$refreferencesif/then/elseThe
patternPropertiessupport is particularly relevant for Signal K's path patterns likeelectrical.batteries.*.voltage.AsyncAPI Compatibility
AsyncAPI (for documenting WebSocket protocols) uses JSON Schema for message definitions. TypeBox schemas work directly — no conversion step needed.
Type Design: TypeBox for Objects, Metadata for Primitives
The Real Problem
As David put it: "What kind of object does this path send? Once I know, is it valid?"
For primitives, clients can already use
typeof:typeof value === 'number'✓typeof value === 'string'✓typeof value === 'boolean'✓Primitives don't need TypeBox types — metadata with units and description is enough.
Objects are the main issue. What does a Position contain? What fields are in an AIS target? What's the structure of a Notification? That's where TypeBox adds real value.
The Wrong Approach
Creating a type for every well-known path doesn't add value:
The Right Approach: TypeBox for Objects Only
Focus TypeBox on object schemas where it provides real value:
Primitives use metadata only:
What This Gives Clients
typeofcheck + units/displayUnits from metadatatypeofcheck + description from metadatatypeofcheckFor objects, clients can:
Bidirectional Discovery
Binding paths to types via metadata enables querying both ways:
navigation.positionemit?" → Positionnavigation.position,navigation.destination, etc.This helps clients discover relevant paths: "show me all AIS target paths" or "find all notification paths."
Units: No Overlap
Units live in different places depending on value type:
units: "m/s")For example,
navigation.speedOverGroundis a primitive — units in path metadata.navigation.positionis an object — latitude and longitude properties have their own definitions in the schema. No conflict.Building on What Exists
This is not a new invention — it's a formalization of what
keyswithmetadata.jsonalready does. That file already contains path descriptions, units, and type information. The proposal:keyswithmetadata.jsonfrom the metadata registry (backwards compatible)Existing code consuming
keyswithmetadata.jsoncontinues working unchanged. New code gets TypeScript support and object validation on top.Evolving Existing Packages
@signalk/signalk-schemaCurrent role: Machine-readable spec, validation utilities, metadata source.
Evolution:
keyswithmetadata.jsonformat, generate it from TypeBox@signalk/server-apiCurrent role: TypeScript types for server APIs and domain objects.
Evolution:
signalk-schemaRelationship
AsyncAPI for WebSocket Protocol
Why This Matters
Signal K's WebSocket protocol is documented only in prose. There's no machine-readable specification, no interactive documentation (like Swagger UI provides for REST), and no support for spec-based integration.
What AsyncAPI Provides
AsyncAPI is the OpenAPI equivalent for event-driven APIs. It's mature (v3.0) and well-established in the IoT world.
Complementary to OpenAPI
Both are needed:
Both would be generated from the same TypeBox schemas, ensuring consistency.
Admin UI Integration
Add a navigation item alongside the existing OpenAPI/Swagger:
Documentation Consolidation
Current Problem
Documentation is spread across:
Proposed Approach
Consolidate into the server, published via demo.signalk.org:
/admin/openapi/admin/asyncapi/admin/pathsor similarThe specification repo's HTML documentation could redirect to the consolidated location, or continue as a snapshot for versioned releases.
Implementation Stages
Stage 1: TypeBox in
signalk-schema(Proof of Concept)Stage 2: Path Metadata Enhancement
keyswithmetadata.jsonfrom TypeBoxStage 3: Server OpenAPI Migration
Stage 4: AsyncAPI Addition
Stage 5: Documentation Consolidation
Effort Estimate
All stages are incremental. Each provides value independently.
Benefits Summary
What Doesn't Change
@signalk/server-apirole in ecosystemOpen Questions
Metadata Registry Location
Should path metadata live in
signalk-schemaor a separate registry?Recommendation: Keep in
signalk-schemaalongside the value-category schemas. Single package for "what is Signal K data."AsyncAPI UI Choice
Recommendation: Start with React component for consistency with Admin UI.
Schema Versioning
Recommendation: Tie to package version. Schema changes are releases.
References
@signalk/signalk-schema: https://www.npmjs.com/package/@signalk/signalk-schema@signalk/server-api: https://www.npmjs.com/package/@signalk/server-apiPrepared for Signal K maintainer discussion
January 2025