Fleet Health
Stadium-board health across a meshed pack of nodes — without letting a down neighbour take your local /health with it.
Per-node readiness (withReadiness → GET /health / Node.status) stays local. FleetHealth is a separate glass on the same mesh Telemetry uses: leaf for this node, fleet fields that fold peers with Effect Exit kept so silence never lies.
Declare the glass
One tag. Distribute it across the droplets you actually run.
class class DropletEastclass DropletEast {
key: Identifier;
Service: {
protocol: Context.Service.Shape<typeof RpcClient.Protocol>;
ping: Effect.Effect<number, NodeUnreachable>;
status: { readonly get: Effect.Effect<NodeStatusSnapshot, NodeUnreachable>; readonly changes: Stream.Stream<NodeStatusSnapshot, NodeUnreachable> };
logs: { readonly stream: Stream.Stream<LogEntry, NodeUnreachable>; readonly query: (options: { readonly limit: number }) => Effect.Effect<ReadonlyArray<LogEntry>, NodeUnreachable> };
};
}
DropletEast extends import NodeNode.Tag<Self, ROut = never>(): { (key: string): NodeTagClass<Self, ROut, BareAddress>; (key: string, target: { readonly path: string; readonly kind?: 'IpcSocket'; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, IpcAddress>; (key: string, target: number | `:${number}`): NodeTagClass<Self, ROut, HttpAddress>; (key: string, target: `ws://${string}` | `wss://${string}`): NodeTagClass<Self, ROut, WsAddress>; (key: string, target: `http://${string}` | `https://${string}`): NodeTagClass<Self, ROut, HttpAddress>; (key: string, target: { readonly url: `ws://${string}` | `wss://${string}`; readonly kind?: 'WebSocket'; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, WsAddress>; (key: string, target: { readonly url: string; readonly kind: 'WebSocket'; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, WsAddress>; (key: string, target: { readonly url: string; readonly kind: 'Http'; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, HttpAddress>; <T extends ShorthandTarget>(key: string, target: T): NodeTagClass<Self, ROut, MultiAddress<KindsOf<T>>>; (key: string, target: string | { readonly url: string; readonly kind?: ProtocolKind; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, UrlAddressLoose>; (key: string, target?: LooseNodeTarget): NodeTagClass<Self, ROut, BareAddress | IpcAddress | HttpAddress | WsAddress | UrlAddressLoose | MultiAddress<ProtocolKind>> }Declare a node — a named transport endpoint a HyperService connects to. Two-stage and keyed by
a string, mirroring Effect's Context.Service<Self, Shape>()(key) (a node is a Context.Key,
resolved by its key in the Context map) and every sibling factory (Hyperlink.Tag<Self>(), …).
The second call infers the target shape, so the { http, ws } shorthand types its
ProtocolKind
set precisely. Optional catalog type param ROut (C2) — prefer import type
for those handles (C4). Templates (no address until cloned) live on
Node
.Prototype:
class EdgeNode extends Node.Tag<EdgeNode>()("edge") {} // no address yet
class Worker extends Node.Tag<Worker>()("worker", 3001) {} // → http://localhost:3001/rpc, kind "Http"
class Mail extends Node.Tag<Mail>()("mail", "https://mail.internal/rpc") {} // full url, as-is, kind "Http"
class Live extends Node.Tag<Live>()("live", { url: "wss://live/rpc" }) {} // kind "WebSocket" (inferred from ws url)
class Push extends Node.Tag<Push>()("push", { url: "/rpc", kind: "WebSocket" }) {} // same-origin path, explicit kind
class Local extends Node.Tag<Local>()("local", { path: "/tmp/local.sock" }) {} // kind "IpcSocket" (Unix domain)
class Droplet extends Node.Tag<Droplet>()("droplet", { http: "http://d/rpc", ws: "ws://d/rpc" }) {} // multi-protocol
import type { Jobs, Emails } from "@app/contracts"
class AppWorker extends Node.Tag<AppWorker, Jobs | Emails>()("app/Worker", { path: "/tmp/w.sock" }) {}
class MailWorker extends Node.Prototype<MailWorker, Mail>("app/MailWorker") {}
The key is the service key. The optional address matches a dial target: a port
(3001 or ":3001" → http://localhost:3001/rpc), a full url (used as-is), { url, kind } for
an explicit endpoint, { path } for a Unix-domain socket (kind: "IpcSocket"), or the
{ http, ws, ipc } multi-protocol shorthand. The node carries
ProtocolKind
so the topology
is self-describing about where AND how:
connect
(node) derives the transport with no
protocol argument.
Dialable targets return an
AddressedNode
(kind: ProtocolKind) so
Hyperlink.client(Tag, Worker) can auto-wire
connect
. Bare Node.Tag()("x")
stays address-less (kind: undefined) — still needs explicit connect / lookup.
Tag<class DropletEastclass DropletEast {
key: Identifier;
Service: {
protocol: Context.Service.Shape<typeof RpcClient.Protocol>;
ping: Effect.Effect<number, NodeUnreachable>;
status: { readonly get: Effect.Effect<NodeStatusSnapshot, NodeUnreachable>; readonly changes: Stream.Stream<NodeStatusSnapshot, NodeUnreachable> };
logs: { readonly stream: Stream.Stream<LogEntry, NodeUnreachable>; readonly query: (options: { readonly limit: number }) => Effect.Effect<ReadonlyArray<LogEntry>, NodeUnreachable> };
};
}
DropletEast>()("app/DropletEast") {}
class class DropletWestclass DropletWest {
key: Identifier;
Service: {
protocol: Context.Service.Shape<typeof RpcClient.Protocol>;
ping: Effect.Effect<number, NodeUnreachable>;
status: { readonly get: Effect.Effect<NodeStatusSnapshot, NodeUnreachable>; readonly changes: Stream.Stream<NodeStatusSnapshot, NodeUnreachable> };
logs: { readonly stream: Stream.Stream<LogEntry, NodeUnreachable>; readonly query: (options: { readonly limit: number }) => Effect.Effect<ReadonlyArray<LogEntry>, NodeUnreachable> };
};
}
DropletWest extends import NodeNode.Tag<Self, ROut = never>(): { (key: string): NodeTagClass<Self, ROut, BareAddress>; (key: string, target: { readonly path: string; readonly kind?: 'IpcSocket'; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, IpcAddress>; (key: string, target: number | `:${number}`): NodeTagClass<Self, ROut, HttpAddress>; (key: string, target: `ws://${string}` | `wss://${string}`): NodeTagClass<Self, ROut, WsAddress>; (key: string, target: `http://${string}` | `https://${string}`): NodeTagClass<Self, ROut, HttpAddress>; (key: string, target: { readonly url: `ws://${string}` | `wss://${string}`; readonly kind?: 'WebSocket'; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, WsAddress>; (key: string, target: { readonly url: string; readonly kind: 'WebSocket'; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, WsAddress>; (key: string, target: { readonly url: string; readonly kind: 'Http'; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, HttpAddress>; <T extends ShorthandTarget>(key: string, target: T): NodeTagClass<Self, ROut, MultiAddress<KindsOf<T>>>; (key: string, target: string | { readonly url: string; readonly kind?: ProtocolKind; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, UrlAddressLoose>; (key: string, target?: LooseNodeTarget): NodeTagClass<Self, ROut, BareAddress | IpcAddress | HttpAddress | WsAddress | UrlAddressLoose | MultiAddress<ProtocolKind>> }Declare a node — a named transport endpoint a HyperService connects to. Two-stage and keyed by
a string, mirroring Effect's Context.Service<Self, Shape>()(key) (a node is a Context.Key,
resolved by its key in the Context map) and every sibling factory (Hyperlink.Tag<Self>(), …).
The second call infers the target shape, so the { http, ws } shorthand types its
ProtocolKind
set precisely. Optional catalog type param ROut (C2) — prefer import type
for those handles (C4). Templates (no address until cloned) live on
Node
.Prototype:
class EdgeNode extends Node.Tag<EdgeNode>()("edge") {} // no address yet
class Worker extends Node.Tag<Worker>()("worker", 3001) {} // → http://localhost:3001/rpc, kind "Http"
class Mail extends Node.Tag<Mail>()("mail", "https://mail.internal/rpc") {} // full url, as-is, kind "Http"
class Live extends Node.Tag<Live>()("live", { url: "wss://live/rpc" }) {} // kind "WebSocket" (inferred from ws url)
class Push extends Node.Tag<Push>()("push", { url: "/rpc", kind: "WebSocket" }) {} // same-origin path, explicit kind
class Local extends Node.Tag<Local>()("local", { path: "/tmp/local.sock" }) {} // kind "IpcSocket" (Unix domain)
class Droplet extends Node.Tag<Droplet>()("droplet", { http: "http://d/rpc", ws: "ws://d/rpc" }) {} // multi-protocol
import type { Jobs, Emails } from "@app/contracts"
class AppWorker extends Node.Tag<AppWorker, Jobs | Emails>()("app/Worker", { path: "/tmp/w.sock" }) {}
class MailWorker extends Node.Prototype<MailWorker, Mail>("app/MailWorker") {}
The key is the service key. The optional address matches a dial target: a port
(3001 or ":3001" → http://localhost:3001/rpc), a full url (used as-is), { url, kind } for
an explicit endpoint, { path } for a Unix-domain socket (kind: "IpcSocket"), or the
{ http, ws, ipc } multi-protocol shorthand. The node carries
ProtocolKind
so the topology
is self-describing about where AND how:
connect
(node) derives the transport with no
protocol argument.
Dialable targets return an
AddressedNode
(kind: ProtocolKind) so
Hyperlink.client(Tag, Worker) can auto-wire
connect
. Bare Node.Tag()("x")
stays address-less (kind: undefined) — still needs explicit connect / lookup.
Tag<class DropletWestclass DropletWest {
key: Identifier;
Service: {
protocol: Context.Service.Shape<typeof RpcClient.Protocol>;
ping: Effect.Effect<number, NodeUnreachable>;
status: { readonly get: Effect.Effect<NodeStatusSnapshot, NodeUnreachable>; readonly changes: Stream.Stream<NodeStatusSnapshot, NodeUnreachable> };
logs: { readonly stream: Stream.Stream<LogEntry, NodeUnreachable>; readonly query: (options: { readonly limit: number }) => Effect.Effect<ReadonlyArray<LogEntry>, NodeUnreachable> };
};
}
DropletWest>()("app/DropletWest") {}
class class MeshHealthclass MeshHealth {
key: Identifier;
Service: {
local: Effect<FleetHealth.LocalHealth, never, never>;
byNode: Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>;
status: Effect<'ok' | 'degraded' | 'partial', never, never>;
};
}
MeshHealth extends import FleetHealthFleetHealth.const Tag: <Self>() => {
(): FleetHealthTag<Self>
<HSelf>(options: {
readonly node: NodeKey<HSelf>
readonly description?: string
}): FleetHealthNodeTag<Self, HSelf>
}
Declare a FleetHealth tag:
class MeshHealth extends FleetHealth.Tag<MeshHealth>()().pipe(
Hyperlink.nodes([DropletEast, DropletWest]),
) {}
Tag<class MeshHealthclass MeshHealth {
key: Identifier;
Service: {
local: Effect<FleetHealth.LocalHealth, never, never>;
byNode: Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>;
status: Effect<'ok' | 'degraded' | 'partial', never, never>;
};
}
MeshHealth>()().Pipeable.pipe<FleetHealth.FleetHealthTag<MeshHealth>, FleetHealth.FleetHealthTag<MeshHealth>>(this: FleetHealth.FleetHealthTag<MeshHealth>, ab: (_: FleetHealth.FleetHealthTag<MeshHealth>) => FleetHealth.FleetHealthTag<MeshHealth>): FleetHealth.FleetHealthTag<MeshHealth> (+21 overloads)pipe(
import HyperlinkHyperlink.const nodes: {
<HSelf>(
nodeSet: readonly [AddressedNode<HSelf>]
): <T extends PipeableTag>(
tag: T
) => SoleNodeBind<T, AddressedNode<HSelf>>
<HSelf>(nodeSet: readonly [NodeKey<HSelf>]): <
T extends PipeableTag
>(
tag: T
) => SoleNodeBind<T, NodeKey<HSelf>>
<T extends PipeableTag>(
nodeSet: ReadonlyArray<AnyNode>
): (tag: T) => T
<Self, S extends Spec, HSelf>(
tag:
| HyperlinkTag<Self, S>
| NodeBoundTag<Self, S, HSelf>,
nodeSet: readonly [AddressedNode<HSelf>]
): SoleNodeBind<
NodeBoundTag<Self, S, HSelf>,
AddressedNode<HSelf>
>
<Self, S extends Spec, HSelf>(
tag:
| HyperlinkTag<Self, S>
| NodeBoundTag<Self, S, HSelf>,
nodeSet: readonly [NodeKey<HSelf>]
): SoleNodeBind<
NodeBoundTag<Self, S, HSelf>,
NodeKey<HSelf>
>
<Self, S extends Spec, HSelf>(
tag: NodeBoundTag<Self, S, HSelf>,
nodeSet: ReadonlyArray<AnyNode>
): NodeBoundTag<Self, S, HSelf>
<Self, S extends Spec>(
tag: HyperlinkTag<Self, S>,
nodeSet: ReadonlyArray<AnyNode>
): HyperlinkTag<Self, S>
}
nodes([class DropletEastclass DropletEast {
key: Identifier;
Service: {
protocol: Context.Service.Shape<typeof RpcClient.Protocol>;
ping: Effect.Effect<number, NodeUnreachable>;
status: { readonly get: Effect.Effect<NodeStatusSnapshot, NodeUnreachable>; readonly changes: Stream.Stream<NodeStatusSnapshot, NodeUnreachable> };
logs: { readonly stream: Stream.Stream<LogEntry, NodeUnreachable>; readonly query: (options: { readonly limit: number }) => Effect.Effect<ReadonlyArray<LogEntry>, NodeUnreachable> };
};
of: (this: void, self: NodeProtocol) => NodeProtocol;
context: (self: NodeProtocol) => Context<DropletEast>;
use: (f: (service: NodeProtocol) => Effect<A, E, R>) => Effect<A, E, DropletEast | R>;
useSync: (f: (service: NodeProtocol) => A) => Effect<A, never, DropletEast>;
Identifier: Identifier;
stack: string | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
url: undefined;
path: undefined;
kind: undefined;
logs: unknown;
onConflict: OnConflict;
}
DropletEast, class DropletWestclass DropletWest {
key: Identifier;
Service: {
protocol: Context.Service.Shape<typeof RpcClient.Protocol>;
ping: Effect.Effect<number, NodeUnreachable>;
status: { readonly get: Effect.Effect<NodeStatusSnapshot, NodeUnreachable>; readonly changes: Stream.Stream<NodeStatusSnapshot, NodeUnreachable> };
logs: { readonly stream: Stream.Stream<LogEntry, NodeUnreachable>; readonly query: (options: { readonly limit: number }) => Effect.Effect<ReadonlyArray<LogEntry>, NodeUnreachable> };
};
of: (this: void, self: NodeProtocol) => NodeProtocol;
context: (self: NodeProtocol) => Context<DropletWest>;
use: (f: (service: NodeProtocol) => Effect<A, E, R>) => Effect<A, E, DropletWest | R>;
useSync: (f: (service: NodeProtocol) => A) => Effect<A, never, DropletWest>;
Identifier: Identifier;
stack: string | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
url: undefined;
path: undefined;
kind: undefined;
logs: unknown;
onConflict: OnConflict;
}
DropletWest]),
) {}Serve with peers + optional readiness
Pass the same readiness rows /health uses when you want the leaf to match Node.status. Discharge the mesh with Hyperlink.peersLayer (or FleetHealth.alone for a single node).
const const readiness: Effect.Effect<
Array<{
key: string
kind: string
ready: boolean
}>,
never,
never
>
const readiness: {
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
readiness = import EffectEffect.const succeed: <A>(value: A) => Effect<A>Creates an Effect that always succeeds with a given value.
When to use
Use when an effect should complete successfully with a specific value without any errors
or external dependencies.
Example (Creating a successful effect)
import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)
succeed([
{ key: stringkey: "app/Cache", kind: stringkind: "hyperlink-ts/Hyperlink", ready: booleanready: true },
])
import FleetHealthFleetHealth.const serve: <Self>(
tag: FleetHealthTag<Self>,
options?: FleetHealthOptions
) => Layer.Layer<
| Self
| Hyperlink.Local<Self>
| Handler<"status">
| Handler<"local">
| Handler<"byNode">,
never,
| Hyperlink.PeersId<Self>
| Hyperlink.SelfNodeId<Self>
>
Serve and grant the local instance — counterpart to
Hyperlink.serve
.
FleetHealth.serve(MeshHealth, { readiness }).pipe(
Layer.provide(Hyperlink.peersLayer(MeshHealth, DropletEast)),
)
serve(class MeshHealthclass MeshHealth {
key: Identifier;
Service: {
local: Effect.Effect<FleetHealth.LocalHealth, never, never>;
byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>;
status: Effect.Effect<'ok' | 'degraded' | 'partial', never, never>;
};
description: string | undefined;
of: (this: void, self: { readonly local: Effect.Effect<FleetHealth.LocalHealth, never, never>; readonly byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>; readonly status: Effect.Eff…;
context: (self: { readonly local: Effect.Effect<FleetHealth.LocalHealth, never, never>; readonly byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>; readonly status: Effect.Effect<'ok' | '…;
use: (f: (service: { readonly local: Effect.Effect<FleetHealth.LocalHealth, never, never>; readonly byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>; readonly status: Effect.Effect<'…;
useSync: (f: (service: { readonly local: Effect.Effect<FleetHealth.LocalHealth, never, never>; readonly byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>; readonly status: Effect.Effect<'…;
Identifier: Identifier;
stack: string | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
MeshHealth, { FleetHealthOptions.readiness?: Effect.Effect<Array<{ key: string; kind: string; ready: boolean }>, never, never>(property) FleetHealthOptions.readiness?: {
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
readiness }).Pipeable.pipe<Layer.Layer<MeshHealth | Hyperlink.Local<MeshHealth> | Handler<"status"> | Handler<"local"> | Handler<"byNode">, never, Hyperlink.PeersId<MeshHealth> | Hyperlink.SelfNodeId<MeshHealth>>, Layer.Layer<MeshHealth | Hyperlink.Local<MeshHealth> | Handler<"status"> | Handler<"local"> | Handler<"byNode">, never, never>>(this: Layer.Layer<...>, ab: (_: Layer.Layer<MeshHealth | Hyperlink.Local<...> | Handler<...> | Handler<...> | Handler<...>, never, Hyperlink.PeersId<...> | Hyperlink.SelfNodeId<...>>) => Layer.Layer<...>): Layer.Layer<...> (+21 overloads)pipe(
import LayerLayer.const provide: {
<RIn, E, ROut>(that: Layer<ROut, E, RIn>): <
RIn2,
E2,
ROut2
>(
self: Layer<ROut2, E2, RIn2>
) => Layer<
ROut2,
E | E2,
RIn | Exclude<RIn2, ROut>
>
<Layers extends [Any, ...Array<Any>]>(
that: Layers
): <A, E, R>(
self: Layer<A, E, R>
) => Layer<
A,
E | Error<Layers[number]>,
| Services<Layers[number]>
| Exclude<R, Success<Layers[number]>>
>
<RIn2, E2, ROut2, RIn, E, ROut>(
self: Layer<ROut2, E2, RIn2>,
that: Layer<ROut, E, RIn>
): Layer<
ROut2,
E | E2,
RIn | Exclude<RIn2, ROut>
>
<A, E, R, Layers extends [Any, ...Array<Any>]>(
self: Layer<A, E, R>,
that: Layers
): Layer<
A,
E | Error<Layers[number]>,
| Services<Layers[number]>
| Exclude<R, Success<Layers[number]>>
>
}
Feeds the output services of the dependency layer into the requirements of
this layer, returning a layer that only provides the services from this layer.
When to use
Use when you need to hide an implementation dependency layer from callers.
Details
In serviceLayer.pipe(Layer.provide(dependencyLayer)), the dependency layer is
built first and is used to satisfy the requirements of serviceLayer.
Example (Providing layer dependencies)
import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
class UserService extends Context.Service<UserService, {
readonly getUser: (id: string) => Effect.Effect<{
id: string
name: string
}>
}>()("UserService") {}
class Logger extends Context.Service<Logger, {
readonly log: (msg: string) => Effect.Effect<void>
}>()("Logger") {}
// Create dependency layers
const databaseLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
})
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(`[LOG] ${msg}`)))
})
// UserService depends on Database and Logger
const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
const database = yield* Database
const logger = yield* Logger
return {
getUser: Effect.fn("UserService.getUser")(function*(id: string) {
yield* logger.log(`Looking up user ${id}`)
const result = yield* database.query(
`SELECT * FROM users WHERE id = ${id}`
)
return { id, name: result }
})
}
}))
// Provide dependencies to UserService layer
const userServiceWithDependencies = userServiceLayer.pipe(
Layer.provide(Layer.mergeAll(databaseLayer, loggerLayer))
)
// Now UserService layer has no dependencies
const program = Effect.gen(function*() {
const userService = yield* UserService
return yield* userService.getUser("123")
}).pipe(
Effect.provide(userServiceWithDependencies)
)
provide(import HyperlinkHyperlink.const peersLayer: <
Self,
S extends Spec,
EIn = never,
RIn = never
>(
tag: HyperlinkTag<Self, S>,
self: AnyNode,
options?: {
readonly nodes?: ReadonlyArray<AnyNode>
readonly url?: (
node: AnyNode
) => Effect.Effect<
string | undefined,
EIn,
RIn
>
}
) => Layer.Layer<
PeersId<Self> | SelfNodeId<Self>,
EIn,
RIn
>
Provide the
peers
capability on this node: connect every OTHER node in the tag's
distributed
/
nodes
set and expose them as the peer clients. Also provides the
selfNode
capability (this node's key) for byNode-style folds. The opt-in mesh — add
it to a node's serve only where the HyperService's own logic reaches across nodes. self is the node
you are, so you're excluded from your own peer set.
Membership (D3):
- Fixed — non-empty
options.nodes or stamped nodes([…]) / distributed([…]).
- Directory — stamped empty set (bare
.pipe(Hyperlink.distributed) / nodes([])): read
Lookup Directory.nodesServing(tag.key) at layer build. Soft empty map when Directory is absent.
- Undeclared — no
nodesSym and no options.nodes → empty static peers (not directory).
Peer addresses: each
Node
's own url / path is the default. Pass options.url to
override the url per node — an env-specific port, a tunnel, or a value from Effect Config —
falling back to Node.url when the resolver returns undefined. A node with no dialable address
is skipped (never a throw), so a partial mesh degrades cleanly. IpcSocket peers dial via
protocolIpc
when only path is set. The resolver's error and requirements flow to the
layer (typed).
peersLayer(class MeshHealthclass MeshHealth {
key: Identifier;
Service: {
local: Effect.Effect<FleetHealth.LocalHealth, never, never>;
byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>;
status: Effect.Effect<'ok' | 'degraded' | 'partial', never, never>;
};
description: string | undefined;
of: (this: void, self: { readonly local: Effect.Effect<FleetHealth.LocalHealth, never, never>; readonly byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>; readonly status: Effect.Eff…;
context: (self: { readonly local: Effect.Effect<FleetHealth.LocalHealth, never, never>; readonly byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>; readonly status: Effect.Effect<'ok' | '…;
use: (f: (service: { readonly local: Effect.Effect<FleetHealth.LocalHealth, never, never>; readonly byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>; readonly status: Effect.Effect<'…;
useSync: (f: (service: { readonly local: Effect.Effect<FleetHealth.LocalHealth, never, never>; readonly byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>; readonly status: Effect.Effect<'…;
Identifier: Identifier;
stack: string | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
MeshHealth, class DropletEastclass DropletEast {
key: Identifier;
Service: {
protocol: Context.Service.Shape<typeof RpcClient.Protocol>;
ping: Effect.Effect<number, NodeUnreachable>;
status: { readonly get: Effect.Effect<NodeStatusSnapshot, NodeUnreachable>; readonly changes: Stream.Stream<NodeStatusSnapshot, NodeUnreachable> };
logs: { readonly stream: Stream.Stream<LogEntry, NodeUnreachable>; readonly query: (options: { readonly limit: number }) => Effect.Effect<ReadonlyArray<LogEntry>, NodeUnreachable> };
};
of: (this: void, self: NodeProtocol) => NodeProtocol;
context: (self: NodeProtocol) => Context<DropletEast>;
use: (f: (service: NodeProtocol) => Effect.Effect<A, E, R>) => Effect.Effect<A, E, DropletEast | R>;
useSync: (f: (service: NodeProtocol) => A) => Effect.Effect<A, never, DropletEast>;
Identifier: Identifier;
stack: string | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
url: undefined;
path: undefined;
kind: undefined;
logs: unknown;
onConflict: OnConflict;
}
DropletEast)),
)Read the board
| Field | Scope | Meaning |
|---|---|---|
local | leaf | This nodes ok / degraded + service rows |
byNode | fleet | Reachable (peers local) or Unreachable (Exit failure) |
status | fleet | ok · degraded · partial (any unreachable) |
Unreachable ≠ ready: false. A cold cache is degraded; a dead peer is unreachable.
const const glass: {
readonly local: Effect.Effect<
FleetHealth.LocalHealth,
never,
never
>
readonly byNode: Effect.Effect<
{
readonly [x: string]:
| FleetHealth.Reachable
| FleetHealth.Unreachable
},
never,
never
>
readonly status: Effect.Effect<
"ok" | "degraded" | "partial",
never,
never
>
}
glass = yield* class MeshHealthclass MeshHealth {
key: Identifier;
Service: {
local: Effect.Effect<FleetHealth.LocalHealth, never, never>;
byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>;
status: Effect.Effect<'ok' | 'degraded' | 'partial', never, never>;
};
description: string | undefined;
of: (this: void, self: { readonly local: Effect.Effect<FleetHealth.LocalHealth, never, never>; readonly byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>; readonly status: Effect.Eff…;
context: (self: { readonly local: Effect.Effect<FleetHealth.LocalHealth, never, never>; readonly byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>; readonly status: Effect.Effect<'ok' | '…;
use: (f: (service: { readonly local: Effect.Effect<FleetHealth.LocalHealth, never, never>; readonly byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>; readonly status: Effect.Effect<'…;
useSync: (f: (service: { readonly local: Effect.Effect<FleetHealth.LocalHealth, never, never>; readonly byNode: Effect.Effect<{ readonly [x: string]: FleetHealth.Reachable | FleetHealth.Unreachable }, never, never>; readonly status: Effect.Effect<'…;
Identifier: Identifier;
stack: string | undefined;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
MeshHealth
const const local: FleetHealth.LocalHealthconst local: {
status: 'ok' | 'degraded';
services: ReadonlyArray<{ readonly key: string; readonly kind: string; readonly ready: boolean; readonly detail?: string | undefined; readonly contractHash?: string | undefined }>;
}
local = yield* const glass: {
readonly local: Effect.Effect<
FleetHealth.LocalHealth,
never,
never
>
readonly byNode: Effect.Effect<
{
readonly [x: string]:
| FleetHealth.Reachable
| FleetHealth.Unreachable
},
never,
never
>
readonly status: Effect.Effect<
"ok" | "degraded" | "partial",
never,
never
>
}
glass.local: Effect.Effect<
FleetHealth.LocalHealth,
never,
never
>
(property) local: {
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
local
// local: FleetHealth.LocalHealth — { status: "ok" | "degraded", services: … }
const const byNode: {
readonly [x: string]:
| FleetHealth.Reachable
| FleetHealth.Unreachable
}
byNode = yield* const glass: {
readonly local: Effect.Effect<
FleetHealth.LocalHealth,
never,
never
>
readonly byNode: Effect.Effect<
{
readonly [x: string]:
| FleetHealth.Reachable
| FleetHealth.Unreachable
},
never,
never
>
readonly status: Effect.Effect<
"ok" | "degraded" | "partial",
never,
never
>
}
glass.byNode: Effect.Effect<
{
readonly [x: string]:
| FleetHealth.Reachable
| FleetHealth.Unreachable
},
never,
never
>
(property) byNode: {
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
byNode
// byNode: Record<string, FleetHealth.NodeReport>
// Reachable → { _tag: "Reachable", status, services }
// Unreachable → { _tag: "Unreachable" }
const const status:
| "ok"
| "degraded"
| "partial"
status = yield* const glass: {
readonly local: Effect.Effect<
FleetHealth.LocalHealth,
never,
never
>
readonly byNode: Effect.Effect<
{
readonly [x: string]:
| FleetHealth.Reachable
| FleetHealth.Unreachable
},
never,
never
>
readonly status: Effect.Effect<
"ok" | "degraded" | "partial",
never,
never
>
}
glass.status: Effect.Effect<
"ok" | "degraded" | "partial",
never,
never
>
(property) status: {
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
status
// status: "ok" | "degraded" | "partial"
Peers only expose the leaf (local). byNode / status are Hyperlink.fleet — excluded from fan-out so a fold cant re-aggregate an aggregate. When you need to keep every peer Exit yourself, use MultiNode.combineByNodeExit (FleetHealth does); combineByNode / Hyperlink.fleetHealth still skip-omit for metric-style folds.
What not to do
Do not fold peers inside withReadiness — that cascades one nodes failure into neighbours /health. Standards forbid it; FleetHealth exists so the fold is explicit and client-shaped.
Runnable form: pnpm run example:fleet-health-glass. See also Telemetry and Fleets & Peers.