Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
## 1.55.0

### Changed

- **Transport now rides on [omnyhub](https://pub.dev/packages/omnyhub) 1.1.0.**
The hand-written `dart:io` WebSocket adapter (`WebSocketConnection`) is replaced
by `FrameConnection`, which wraps an omnyhub `Connection` in a
`TypedConnection<OmnyShellFrame>`: OmnyShell's `FrameCodec` is adapted to
omnyhub's `ConnectionCodec`, mapping control↔text and data↔binary frames.
Node dials and the Hub listener both hand off `OmnyShellConnection`s produced by
omnyhub's transport, so the two packages share one connection/TLS stack. The
protocol core (frames, control messages, channels, broker, runtimes, tunnels)
and the browser transport (`WsChannelConnection`) are unchanged; the full
existing suite — including the wss/TLS-dir/e2e/TestCluster integration tests —
passes as the behavioral gate.
- Bumped `omnydrive` to `^1.12.0` (which is itself now hosted on omnyhub), so all
three packages share the same omnyhub transport/HTTP stack.

## 1.54.0

### Changed
Expand Down
9 changes: 5 additions & 4 deletions lib/src/application/node/node_runtime.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ import '../../infrastructure/identity/machine_id.dart';
import '../../infrastructure/identity/uid_computer.dart';
import '../../infrastructure/backend/process_inspector.dart';
import '../../infrastructure/identity/uid_store.dart';
import '../../infrastructure/transport/web_socket_connection.dart';
import '../../infrastructure/transport/frame_connection.dart';
import '../../protocol/channel.dart';
import '../../protocol/channel_multiplexer.dart';
import '../../protocol/control_message.dart';
import '../../protocol/omnyshell_connection.dart';
import '../../protocol/omnyshell_frame.dart';
import '../../version.dart';
import '../../protocol/protocol_version.dart';
Expand Down Expand Up @@ -182,7 +183,7 @@ class NodeRuntime {
final NodeConfig config;

NodeState _state = NodeState.disconnected;
WebSocketConnection? _connection;
OmnyShellConnection? _connection;
ChannelMultiplexer? _mux;
StreamSubscription? _controlSub;
Timer? _heartbeatTimer;
Expand Down Expand Up @@ -262,9 +263,9 @@ class NodeRuntime {
if (_stopped) return;
await _ensureUid();
_setState(NodeState.connecting);
final WebSocketConnection connection;
final OmnyShellConnection connection;
try {
connection = await WebSocketConnection.connect(
connection = await FrameConnection.connect(
config.hubUri,
securityContext: config.securityContext,
onBadCertificate: config.onBadCertificate,
Expand Down
102 changes: 102 additions & 0 deletions lib/src/infrastructure/transport/frame_connection.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import 'dart:io';

import 'package:omnyhub/omnyhub.dart' as omnyhub;
import 'package:web_socket_channel/web_socket_channel.dart';

import '../../protocol/frame_codec.dart';
import '../../protocol/omnyshell_connection.dart';
import '../../protocol/omnyshell_frame.dart';

/// Bridges OmnyShell's [FrameCodec] to omnyhub's raw [omnyhub.Message] model, so
/// OmnyShell frames ride on omnyhub's transport.
class _FrameConnectionCodec implements omnyhub.ConnectionCodec<OmnyShellFrame> {
final FrameCodec codec;

_FrameConnectionCodec(this.codec);

@override
omnyhub.Message encode(OmnyShellFrame frame) {
final encoded = codec.encode(frame); // String (control) or Uint8List (data)
return encoded is String
? omnyhub.TextMessage(encoded)
: omnyhub.BinaryMessage(encoded as List<int>);
}

@override
OmnyShellFrame decode(omnyhub.Message message) => switch (message) {
omnyhub.TextMessage(:final data) => codec.decode(data),
omnyhub.BinaryMessage(:final data) => codec.decode(data),
};
}

/// An [OmnyShellConnection] backed by an omnyhub [omnyhub.Connection] (WebSocket
/// on the VM), applying [FrameCodec] at the boundary via omnyhub's
/// [omnyhub.TypedConnection].
///
/// This replaces OmnyShell's hand-written `dart:io` WebSocket adapter: the
/// listener wraps each accepted omnyhub connection with [FrameConnection.fromChannel],
/// and clients dial with [FrameConnection.connect]. Undecodable frames are
/// dropped (never tearing the connection down), and text↔control /
/// binary↔data mapping is preserved.
class FrameConnection implements OmnyShellConnection {
final omnyhub.TypedConnection<OmnyShellFrame> _typed;

FrameConnection._(this._typed);

/// Wraps an omnyhub [connection], applying [codec] (defaults to the standard
/// registry).
factory FrameConnection.wrap(
omnyhub.Connection connection, {
FrameCodec? codec,
}) => FrameConnection._(
omnyhub.TypedConnection<OmnyShellFrame>(
connection,
_FrameConnectionCodec(codec ?? FrameCodec.standard()),
),
);

/// Wraps an already-upgraded WebSocket [channel] (Hub server side).
factory FrameConnection.fromChannel(
WebSocketChannel channel, {
FrameCodec? codec,
}) => FrameConnection.wrap(
omnyhub.WebSocketConnection.fromChannel(channel),
codec: codec,
);

/// Dials [uri] (a `wss://`/`ws://` URL) and returns a connected
/// [FrameConnection] (client / node side).
static Future<FrameConnection> connect(
Uri uri, {
Map<String, dynamic>? headers,
FrameCodec? codec,
SecurityContext? securityContext,
Duration? pingInterval,
bool Function(X509Certificate cert, String host, int port)?
onBadCertificate,
}) async {
final connection = await omnyhub.WebSocketConnection.connect(
uri,
headers: headers,
securityContext: securityContext,
pingInterval: pingInterval,
onBadCertificate: onBadCertificate,
);
return FrameConnection.wrap(connection, codec: codec);
}

@override
Stream<OmnyShellFrame> get incoming => _typed.incoming;

@override
bool get isOpen => _typed.isOpen;

@override
Future<void> get done => _typed.done;

@override
void send(OmnyShellFrame frame) => _typed.send(frame);

@override
Future<void> close([int? code, String? reason]) => _typed.close(code, reason);
}
4 changes: 2 additions & 2 deletions lib/src/infrastructure/transport/io_connection_factory.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'dart:io';

import 'connection_factory.dart';
import 'web_socket_connection.dart';
import 'frame_connection.dart';

/// Builds a `dart:io` [ConnectionFactory] with explicit TLS trust overrides.
///
Expand All @@ -14,7 +14,7 @@ ConnectionFactory ioConnectionFactory({
bool Function(X509Certificate cert, String host, int port)? onBadCertificate,
Duration? pingInterval,
}) =>
(Uri uri, {Map<String, String>? headers}) => WebSocketConnection.connect(
(Uri uri, {Map<String, String>? headers}) => FrameConnection.connect(
uri,
headers: headers,
securityContext: securityContext,
Expand Down
4 changes: 2 additions & 2 deletions lib/src/infrastructure/transport/transport_factory_io.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import '../../protocol/omnyshell_connection.dart';
import 'web_socket_connection.dart';
import 'frame_connection.dart';

/// The native default: dials a `wss://` WebSocket over `dart:io` with standard
/// TLS verification. TLS trust overrides (self-signed test certs, pinning) are
/// supplied explicitly via `ioConnectionFactory` in `io_connection_factory.dart`.
Future<OmnyShellConnection> defaultConnectionFactory(
Uri uri, {
Map<String, String>? headers,
}) => WebSocketConnection.connect(uri, headers: headers);
}) => FrameConnection.connect(uri, headers: headers);
110 changes: 0 additions & 110 deletions lib/src/infrastructure/transport/web_socket_connection.dart

This file was deleted.

9 changes: 5 additions & 4 deletions lib/src/infrastructure/transport/ws_channel_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import '../../protocol/omnyshell_connection.dart';
import '../../protocol/omnyshell_frame.dart';

/// An [OmnyShellConnection] over any [WebSocketChannel], with **no** `dart:io`
/// dependency — the browser-safe counterpart of `WebSocketConnection`.
/// dependency — the browser-safe counterpart of the native `FrameConnection`.
///
/// The native `WebSocketConnection` carries `dart:io` at file scope (it builds
/// an `HttpClient` for TLS pinning), which poisons the dart2js build, so the
/// web transport wraps the cross-platform [WebSocketChannel] here instead.
/// The native `FrameConnection` rides on omnyhub's `dart:io` WebSocket
/// transport (which builds an `HttpClient` for TLS pinning), poisoning the
/// dart2js build, so the web transport wraps the cross-platform
/// [WebSocketChannel] here instead.
/// Both translate raw text/binary events to and from [OmnyShellFrame]s with a
/// [FrameCodec]; undecodable frames are dropped rather than tearing the
/// connection down.
Expand Down
13 changes: 7 additions & 6 deletions lib/src/infrastructure/transport/ws_server_endpoint.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_web_socket/shelf_web_socket.dart';

import '../../protocol/frame_codec.dart';
import 'web_socket_connection.dart';
import '../../protocol/omnyshell_connection.dart';
import 'frame_connection.dart';

/// Called for every accepted WebSocket connection.
typedef OnConnection = void Function(WebSocketConnection connection);
typedef OnConnection = void Function(OmnyShellConnection connection);

/// A Hub-side TLS WebSocket listener.
///
/// Binds an HTTPS server with the provided [SecurityContext] and upgrades
/// incoming WebSocket requests, handing each accepted [WebSocketConnection] to
/// the supplied [OnConnection] callback. There is no plaintext mode: a
/// [SecurityContext] is mandatory.
/// incoming WebSocket requests, handing each accepted [OmnyShellConnection]
/// (an omnyhub-backed [FrameConnection]) to the supplied [OnConnection]
/// callback. There is no plaintext mode: a [SecurityContext] is mandatory.
class WsServerEndpoint {
final HttpServer _server;

Expand Down Expand Up @@ -49,7 +50,7 @@ class WsServerEndpoint {
}) async {
final handler = webSocketHandler((channel, _) {
onConnection(
WebSocketConnection.fromChannel(
FrameConnection.fromChannel(
channel,
codec: codecFactory?.call() ?? FrameCodec.standard(),
),
Expand Down
2 changes: 1 addition & 1 deletion lib/src/version.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
/// This is the single source of truth for "what build is this": it is rendered
/// in the CLI banner and is the default a node reports as its
/// [NodeConfig.agentVersion] / [PlatformInfo.agentVersion].
const String omnyShellVersion = '1.54.0';
const String omnyShellVersion = '1.55.0';
5 changes: 3 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: >-
connect to a Hub by node identity (not host:port); the Hub authenticates,
authorizes and brokers encrypted sessions to Nodes over WebSocket-on-TLS.
Ships Hub, Node, Client and CLI implementations behind first-class Dart APIs.
version: 1.54.0
version: 1.55.0
repository: https://github.com/OmnyGrid/omnyshell

environment:
Expand All @@ -22,7 +22,8 @@ dependencies:
ffi: ^2.1.0
http: ^1.2.0
meta: ^1.18.3
omnydrive: ^1.11.0
omnydrive: ^1.12.0
omnyhub: ^1.1.0
path: ^1.9.0
pointycastle: ^4.0.0
#portable_pty: ^0.0.5 # waiting crash fix.
Expand Down
Loading
Loading