Skip to content

Releases: OmnyGrid/omnyhub

v1.5.1

Choose a tag to compare

@gmpassos gmpassos released this 13 Jul 04:35
53b8d6f

Patch release. Dependency-only change; no API changes.

Changed

  • multi_domain_secure_server: ^1.0.17 (from ^1.0.16) — improves error handling and logging in _accept and extractSNIHostname, the path every TLS handshake takes before a SecurityContext is chosen, so an SNI read that fails now reports why instead of failing silently.
  • meta: ^1.19.0 (from ^1.16.0).

The suite passes unmodified against these versions.

Full Changelog: v1.5.0...v1.5.1

v1.5.0

Choose a tag to compare

@gmpassos gmpassos released this 13 Jul 03:44

Added

  • LetsEncryptTls.renewBefore. How much validity a certificate must have
    left to be kept; below it, maybeRenew renews. Defaults to 5 days — the
    previous, hard-coded shelf_letsencrypt behaviour — so nothing changes unless
    you set it.

    5 days is a thin margin: a certificate is renewed at most one
    OmnyHub.tlsRenewalInterval (12h by default) after dropping below the
    threshold, and a failed renewal then has very little room to retry before the
    certificate actually expires. Let's Encrypt's own advice is to renew with
    roughly a third of the lifetime left (30 of 90 days). Applications that were
    previously enforcing their own margin — refusing to serve a near-expiry
    certificate, and reissuing — can now express it here instead:

    LetsEncryptTls.onDemand(
      allowDomain: ...,
      cacheDir: '/etc/letsencrypt/live',
      renewBefore: const Duration(days: 15),
    );

    A certificate that is still valid keeps being served while it renews in the
    background; only an expired one is withheld (isHandledDomainCertificate
    already rejects an expired, corrupt or unloadable certificate before any
    SecurityContext is built, so an expired certificate is never served).

v1.4.0

Choose a tag to compare

@gmpassos gmpassos released this 13 Jul 00:28

On-demand TLS release — the seams an application needs to decide its own way
whether a host may be certified, rather than reimplementing the SNI cache and
issuance around LetsEncryptTls. Driven by adopting omnyhub in MenuIci's
sites_server, whose front door had grown a parallel copy of both.

Additive and backward-compatible: DomainPolicy is widened, so existing
synchronous policies still satisfy it, and autoIssue defaults to the 1.3.0
behaviour. Verified by running the full suite unmodified against this release.

Added

  • Asynchronous domain policy. DomainPolicy is now
    FutureOr<bool> Function(String host) (was bool Function(String host)), so
    on-demand issuance can be gated by a real lookup — a database query, a tenant
    API call — instead of only what is knowable synchronously. Previously an
    application whose "may this host be certified?" answer lived behind I/O had to
    keep a hand-rolled cache beside the hub and pre-warm it, which is exactly the
    duplication allowDomain exists to remove. Existing sync policies are
    unaffected: bool is a subtype of FutureOr<bool>.
  • LetsEncryptTls.autoIssue. When false, only certificates already cached
    in cacheDir are served and the CA is never contacted — neither on a
    handshake miss nor from the renewal loop. For a deployment whose certificates
    are provisioned out-of-band (certbot, a secrets mount, a sibling process),
    which previously could not be expressed through this provider at all.

Changed

  • LetsEncryptTls.isAllowed returns Future<bool>. It awaits the policy.
    The synchronous SNI path (TlsProvider.contextFor, which cannot await) no
    longer calls it: contextFor now schedules obtain() in the background, and
    obtain() is the single place the policy is evaluated. The only breaking
    change, and only for code calling isAllowed directly.
  • A rejected host is no longer re-checked on every handshake. With a sync
    policy the pre-check in contextFor was free; with an async one it could be
    an HTTP call per TLS handshake. Rejections are now remembered in a bounded
    cache (capped, so an SNI flood of random hostnames cannot grow it without
    limit).
  • obtain() stays de-duplicated under an async policy. It registers its
    in-flight future before the first suspension, so concurrent handshakes for
    the same host still share one provisioning attempt now that the allow-check
    can itself suspend.

v1.3.0

Choose a tag to compare

@gmpassos gmpassos released this 12 Jul 21:18
eea1d97

Control-plane release — the seams an application needs to host its own node protocol on NodeGateway/NodeRuntime, rather than reimplementing the registry, heartbeat watchdog and RPC correlation around them.

Additive and backward-compatible: every new wire field is omitted when empty, every new hook defaults to null, and every new option defaults to the 1.2.0 behaviour. Verified by running the omnydrive (260 tests) and omnyshell (808 tests) suites unmodified against this release.

Added

  • Heartbeat.payload + NodeGateway.onHeartbeat + NodeConfig.heartbeatPayload — telemetry rides the beat instead of needing a second periodic message. A payload builder that throws or stalls never costs the node its liveness.
  • NodeNotify + NodeRuntime.notify / NodeGateway.notify / onNotify — one-way push. Previously a node could only push by calling request() and discarding a response it did not want.
  • Connection lifecycle hooksNodeGateway.onConnect / onDisconnect / onTimeout, and NodeEvent.node. The registry's events carry a bare descriptor and never fire at all for a socket that connects without registering.
  • NodeGateway.retainNodes — keep a disconnected or timed-out node, marked offline, for a hub that is the system of record for a known fleet. Backed by NodeRegistry.markOffline and NodeEventKind.disconnected.
  • RegisteredNode.state — per-node application state. The registry constructs RegisteredNode itself, so subclassing it to add fields never worked.
  • NodeConfig.connect (injectable transport), pingInterval, isTerminal (a revoked key is not fixed by retrying), descriptorBuilder (re-advertise on reconnect when the node has changed).
  • AppException, WsCloseCodes.forException, TypedConnection.onDecodeError, LoggerBase.

Fixed

  • Frames sent during registration were silently dropped. NodeGateway ran onRegister without awaiting it, so anything arriving while an async handler was in flight raced an unset registration and was discarded (Heartbeat, NodeUpdate) or refused as Not registered (NodeRequest). Frames are now queued and replayed in order, and discarded if registration is rejected.
  • A stray binary frame could take down the gateway. MessageCodec.decode UTF-8-decoded a BinaryMessage outside its guard, so arbitrary bytes escaped as a raw FormatException — past NodeGateway's HubException-only catch — and surfaced as an uncaught async error.
  • An unavailable node was reported to the peer as an auth failure. Every close-code status outside 401/403/404 fell through to unauthorized; 502/503/504 now map to badGateway.

Changed

MessageCodec's docs claimed third parties could register new message types. They cannot — NodeControlMessage is sealed. Application protocols ride on NodeRequest/NodeResponse and NodeNotify.


278 tests (up from 246). Full notes in CHANGELOG.md.

v1.2.0

Choose a tag to compare

@gmpassos gmpassos released this 12 Jul 08:01
3e3337f

Control-plane release — the node protocol grows the pieces an application needs to run a real HUB/Node infrastructure on it (enrolment, node-initiated calls, domain-specific discovery), instead of only the "worker node" shape where the hub calls the node and the node merely heartbeats.

Fully backward-compatible (additive): every new wire field is omitted when empty and every new hook defaults to null, so existing peers and callers are unaffected.

Added

  • Bidirectional RPC. request/response now flow in both directions. A node calls the hub with NodeRuntime.request(action, payload:), answered by the hub's new NodeGateway.onRequest — which receives the calling RegisteredNode, so the hub knows who is asking and can authorize on its principal. Registration is a precondition: a request from a connection that has not registered is rejected ("Not registered") without reaching the handler. The hub→node direction (NodeGateway.request) is unchanged. Previously the only node-initiated message was query, so an application needed a second channel back to the hub.
  • Enrolment. NodeRegister and NodeRegistered carry a Map<String, dynamic> payload, and NodeGateway.onRegister vets registrations — returning the ack payload, or throwing a HubException to reject (the hub replies error, closes, and never registers the node). Node-side: NodeConfig.registerPayload / NodeConfig.onRegistered and NodeRuntime.registration. This is the seam for CA-style enrolment: submit a CSR, get a signed certificate back.
  • Node-side in-band handshake. NodeConfig.onHandshake runs on the raw connection before registering — the counterpart of the hub's existing ConnectionAuthenticator, which had no node-side half — so a node can now answer a challenge/response or key-agreement exchange. Unconsumed frames are replayed to the control protocol.
  • Application-defined discovery. NodeDescriptor.attributes (Map<String, dynamic>, may nest — unlike the flat string-only labels and metadata), NodeQuery.filter, and the NodeMatcher port, so an application owns query semantics the hub cannot know about (version ranges, nested service catalogues). NodeRegistry.discover takes a where predicate.
  • NodeUpdate (t: "update") — a node revises its advertised descriptor without re-registering.
  • Json.optObject for reading free-form JSON object fields.

Changed

  • NodeGateway.codec and NodeRuntime.codec are now typed ConnectionCodec<NodeControlMessage> rather than the concrete MessageCodec, so an application can supply its own wire format (e.g. binary) without forking either endpoint. MessageCodec.standard() remains the default; source-compatible for existing callers.

Fixed

  • In-flight RPCs no longer hang when a connection drops. NodeGateway and NodeRuntime now fail their pending calls with NodeUnavailableException on disconnect, goodbye and heartbeat timeout, instead of leaving callers to wait out their own timeout. NodeRuntime's pending discovery queries were leaked on disconnect and are now cleared too.

Full Changelog: v1.1.0...v1.2.0

v1.1.0

Choose a tag to compare

@gmpassos gmpassos released this 11 Jul 23:49
6b4b67b

Synergy release — shared primitives for protocol-oriented apps + seamless TLS renewal. Backward-compatible.

  • ConnectionCodec<T> + TypedConnection<T> — a first-class "codec over a raw duplex connection" primitive. Node MessageCodec is now a ConnectionCodec<NodeControlMessage>; NodeRuntime consumes a TypedConnection.
  • Gap-free TLS rebindHttpTransport.rebind() binds a fresh shared listener on the same port with the renewed cert and drains the old gracefully; live connections survive certificate renewal.
  • ReloadableFileTls now detects changes by byte content (catches same-size rotations; keeps the old cert on a partial write).

Published to pub.dev/packages/omnyhub 1.1.0. See the CHANGELOG.

v1.0.0

Choose a tag to compare

@gmpassos gmpassos released this 11 Jul 23:15
1f7b231

First stable release — published to pub.dev/packages/omnyhub.

OmnyHub is a reusable, protocol-agnostic HUB framework for building distributed HUB/Node infrastructures over HTTP/HTTPS/WS/WSS behind one architecture and API.

Main features

  • Multi-service hosting on one instance/port, with dynamic register/unregister.
  • Protocol-agnostic transport — HTTP/HTTPS/WS/WSS on one listener; protocol code isolated from business logic.
  • Advanced routing — host/domain/subdomain (exact, *. wildcard, or regexp), path prefix, path parameters (RouterService/ShelfService), protocol, headers, method, auth-state; composable + custom routers.
  • Reverse proxy & gateway — HTTP + WebSocket-upgrade forwarding, X-Forwarded-*, hybrid local/remote.
  • Automatic TLS — static, hot-reload file, and Let's Encrypt (ACME) with dynamic on-demand multi-domain issuance via SNI.
  • Layered authentication — global AuthCoordinator (authenticate/bypass/delegate/block) + per-service handlers + in-band ConnectionAuthenticator; Bearer/Basic/composite + role/predicate authorizers.
  • Node infrastructure — control protocol + codec, registry, discovery, heartbeat, gateway, and a node runtime with RPC and reconnection.
  • Demo CLI, examples, and design docs.
  • Tested with unit/integration/e2e over real servers and sockets (no mocking).

See the CHANGELOG.

v0.3.0

Choose a tag to compare

@gmpassos gmpassos released this 11 Jul 23:07
35ee093

Migration-readiness features so omnydrive and omnyshell can adopt OmnyHub for HUB/Node/transport. Fully backward-compatible; 213 tests.

Path-parameter routing

  • PathPattern (<endpoint>, wildcard tail <path|.*>) + RouterService — intra-service routing with captured params and method dispatch (405/404).
  • ShelfService — host an existing shelf.Handler / shelf_router.Router verbatim.
  • HostPatternRule(RegExp, {part}) — regexp routing over host / domain / subdomain, combinable with a PathRule via &.

Layered authentication framework

  • AuthCoordinator → sealed AuthDecision: Authenticated / Anonymous (bypass) / Delegate (to per-service) / Blocked (pre-check).
  • Per-service authenticator/authorizer on registerService/route; new TooManyRequestsException (429). DefaultAuthCoordinator keeps the no-config path identical to before.
  • ConnectionAuthenticator + a HandshakeConnection buffered wrapper for in-band WebSocket handshakes (single-subscription-safe hand-off to the service).

Conveniences

  • ReloadableFileTls — hot-reload certificate/key files on change (cert-manager/certbot friendly).
  • mapErrors middleware + successEnvelope/errorEnvelope helpers.
  • NodeRegistry extras: activeSessions, connectionId, byConnectionId, updateActiveSessions.

New examples: path_params_example.dart, layered_auth_example.dart.

See the CHANGELOG for the full list.

v0.1.0

Choose a tag to compare

@gmpassos gmpassos released this 11 Jul 20:35

Initial release of OmnyHub — a reusable, protocol-agnostic HUB framework in pure Dart over HTTP/HTTPS/WS/WSS.

Highlights

  • Multi-service hosting on a single port with dynamic register/unregister.
  • Advanced routing — host/domain/subdomain/path/protocol/header/method/auth-state rules with and/or/not + predicate, and a pluggable Router.
  • Reverse proxy & gateway — streaming HTTP + WebSocket-upgrade forwarding, X-Forwarded-*, hop-by-hop stripping; host/path gateways + hybrid.
  • Automatic TLSStaticTls and LetsEncryptTls (ACME) behind a TlsProvider port, with challenge auto-mount, provision-before-bind and renewal hot-reload.
  • Node infrastructure — control protocol + extensible codec, registry, discovery, heartbeat monitoring, gateway, and a node runtime with RPC, peer discovery and reconnection/backoff.
  • Fail-closed auth — Bearer/Basic/composite authenticators and role/predicate authorizers.
  • Demo CLI, runnable examples, and unit/integration/e2e tests over real servers and sockets (no mocking library).

See the CHANGELOG for the full list.