Creating a Hyperlink
Draft. Tip-check before treating as SSOT.
Build one Hyperlink end to end: declare a Tag, fulfil its Contract with an Implementation, place it with a Layer, and call it through a Handle.
Each fence adds one piece. The Tag runs in-process when you finish. The same call site still works when you later serve or client it.
This page teaches the task. Contract method shapes live in Core Concepts. Serve, client, and fleet Layers live in Managing Layers.
Declare the Tag
Put the Contract on the Tag: methods and their schemas. Nothing runs yet. This is the typed name everything else hangs from:
import * as import HyperlinkHyperlink from "hyperlink-ts/Hyperlink"
import { import SchemaSchema } from "effect"
class class Counterclass Counter {
key: Identifier;
Service: {
value: Hyperlink.Subscribable<number>;
increment: (payload: { by: number }) => Effect<void, never, never>;
reset: Effect<void, never, never>;
};
}
Counter extends import HyperlinkHyperlink.Tag<class Counterclass Counter {
key: Identifier;
Service: {
value: Hyperlink.Subscribable<number>;
increment: (payload: { by: number }) => Effect<void, never, never>;
reset: Effect<void, never, never>;
};
}
Counter>()("app/Counter", {
value: anyvalue: import HyperlinkHyperlink.ref(import SchemaSchema.const Number: Schema.Numberconst Number: {
Rebuild: Rebuild;
Iso: Iso;
ast: Ast;
Type: T;
Encoded: E;
DecodingServices: RD;
EncodingServices: RE;
annotate: (annotations: Schema.Annotations.Bottom<number, readonly []>) => Schema.Number;
annotateKey: (annotations: Schema.Annotations.Key<number>) => Schema.Number;
check: (checks_0: Check<number>, ...checks: Array<Check<number>>) => Schema.Number;
rebuild: (ast: Number) => Schema.Number;
make: (input: number, options?: Schema.MakeOptions) => number;
makeOption: (input: number, options?: Schema.MakeOptions) => Option<number>;
makeEffect: (input: number, options?: Schema.MakeOptions) => Effect<number, Schema.SchemaError, never>;
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; <…;
}
Type-level representation of
Number
.
Schema for number values, including NaN, Infinity, and -Infinity.
Details
Default JSON serializer:
- Finite numbers are serialized as numbers.
- Non-finite values are serialized as strings (
"NaN", "Infinity", "-Infinity").
Number),
increment: any(property) increment: {
kind: MethodKind;
payload: P;
success: Su;
error: E;
stream: Str;
annotations: Ann;
annotate: <A extends MethodAnnotations>(annotations: A) => Method<P, Su, E, Str, Ann & A, Client>;
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; <…;
}
increment: import HyperlinkHyperlink.effectFn({ by: Schema.Number(property) by: {
Rebuild: Rebuild;
Iso: Iso;
ast: Ast;
Type: T;
Encoded: E;
DecodingServices: RD;
EncodingServices: RE;
annotate: (annotations: Schema.Annotations.Bottom<number, readonly []>) => Schema.Number;
annotateKey: (annotations: Schema.Annotations.Key<number>) => Schema.Number;
check: (checks_0: Check<number>, ...checks: Array<Check<number>>) => Schema.Number;
rebuild: (ast: Number) => Schema.Number;
make: (input: number, options?: Schema.MakeOptions) => number;
makeOption: (input: number, options?: Schema.MakeOptions) => Option<number>;
makeEffect: (input: number, options?: Schema.MakeOptions) => Effect<number, Schema.SchemaError, never>;
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; <…;
}
by: import SchemaSchema.const Number: Schema.Numberconst Number: {
Rebuild: Rebuild;
Iso: Iso;
ast: Ast;
Type: T;
Encoded: E;
DecodingServices: RD;
EncodingServices: RE;
annotate: (annotations: Schema.Annotations.Bottom<number, readonly []>) => Schema.Number;
annotateKey: (annotations: Schema.Annotations.Key<number>) => Schema.Number;
check: (checks_0: Check<number>, ...checks: Array<Check<number>>) => Schema.Number;
rebuild: (ast: Number) => Schema.Number;
make: (input: number, options?: Schema.MakeOptions) => number;
makeOption: (input: number, options?: Schema.MakeOptions) => Option<number>;
makeEffect: (input: number, options?: Schema.MakeOptions) => Effect<number, Schema.SchemaError, never>;
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; <…;
}
Type-level representation of
Number
.
Schema for number values, including NaN, Infinity, and -Infinity.
Details
Default JSON serializer:
- Finite numbers are serialized as numbers.
- Non-finite values are serialized as strings (
"NaN", "Infinity", "-Infinity").
Number }),
reset: any(property) reset: {
kind: MethodKind;
payload: P;
success: Su;
error: E;
stream: Str;
annotations: Ann;
annotate: <A extends MethodAnnotations>(annotations: A) => Method<P, Su, E, Str, Ann & A, Client>;
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; <…;
}
reset: import HyperlinkHyperlink.effect(import SchemaSchema.const Void: Schema.Voidconst Void: {
Rebuild: Rebuild;
Iso: Iso;
ast: Ast;
Type: T;
Encoded: E;
DecodingServices: RD;
EncodingServices: RE;
annotate: (annotations: Schema.Annotations.Bottom<void, readonly []>) => Schema.Void;
annotateKey: (annotations: Schema.Annotations.Key<void>) => Schema.Void;
check: (checks_0: Check<void>, ...checks: Array<Check<void>>) => Schema.Void;
rebuild: (ast: Void) => Schema.Void;
make: (input: void, options?: Schema.MakeOptions) => void;
makeOption: (input: void, options?: Schema.MakeOptions) => Option<void>;
makeEffect: (input: void, options?: Schema.MakeOptions) => Effect<void, Schema.SchemaError, never>;
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; <…;
}
Type-level representation of
Void
.
Schema for a TypeScript void return value.
When to use
Use when you need to model the return value of a function, RPC, or endpoint
whose result is intentionally ignored.
Details
Runtime parsing accepts any present value and discards it, producing
undefined. The public decoded and encoded TypeScript representation remains
void, so typed construction, decoding, and encoding APIs are still modeled
as void.
Void),
}) {}Fulfil It
Return those methods from an Implementation. A SubscriptionRef backs the observable value:
const const counterImpl: Effect.Effect<
{
value: any
increment: ({
by,
}: {
readonly by: number
}) => Effect.Effect<void, never, never>
reset: Effect.Effect<void, never, never>
},
never,
never
>
const counterImpl: {
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;
}
counterImpl = import EffectEffect.const gen: <Effect.Effect<SubscriptionRef.SubscriptionRef<number>, never, never>, {
value: any;
increment: ({ by }: {
readonly by: number;
}) => Effect.Effect<void, never, never>;
reset: Effect.Effect<void, never, never>;
}>(f: () => Generator<Effect.Effect<SubscriptionRef.SubscriptionRef<number>, never, never>, {
value: any;
increment: ({ by }: {
readonly by: number;
}) => Effect.Effect<void, never, never>;
reset: Effect.Effect<void, never, never>;
}, never>) => Effect.Effect<...> (+1 overload)
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to use
Use when you want to write effectful code that looks and behaves like
synchronous code, while still handling asynchronous tasks, errors, and complex
control flow such as loops and conditions.
Generator functions work similarly to async/await but keep errors,
requirements, and interruption in the Effect type. You can yield* values
from effects and return the final result at the end.
Example (Sequencing effects with generators)
import { Data, Effect } from "effect"
class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, DiscountRateError> =>
discountRate === 0
? Effect.fail(new DiscountRateError())
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function*() {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})
gen(function* () {
const const ref: SubscriptionRef.SubscriptionRef<number>const ref: {
value: A;
semaphore: Semaphore.Semaphore;
pubsub: PubSub.PubSub<A>;
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; <…;
}
ref = yield* import SubscriptionRefSubscriptionRef.const make: <number>(
value: number
) => Effect.Effect<
SubscriptionRef.SubscriptionRef<number>,
never,
never
>
Constructs a new SubscriptionRef from an initial value.
When to use
Use to create a SubscriptionRef when consumers need to read the latest
value and subscribe to every update.
Details
The initial value is published during construction, so changes starts new
subscribers with that value before future updates.
make(0)
return {
value: any(property) value: {
get: Effect.Effect<A>;
changes: Stream.Stream<A>;
}
value: import HyperlinkHyperlink.subscribable(const ref: SubscriptionRef.SubscriptionRef<number>const ref: {
value: A;
semaphore: Semaphore.Semaphore;
pubsub: PubSub.PubSub<A>;
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; <…;
}
ref),
increment: ({
by,
}: {
readonly by: number
}) => Effect.Effect<void, never, never>
increment: ({ by: numberby }: { readonly by: numberby: number }) =>
import SubscriptionRefSubscriptionRef.const update: <number>(self: SubscriptionRef.SubscriptionRef<number>, update: (a: number) => number) => Effect.Effect<void> (+1 overload)Updates the value of the SubscriptionRef with the result of applying a
function, notifying subscribers of the change.
Example (Updating a value)
import { Effect, SubscriptionRef } from "effect"
const program = Effect.gen(function*() {
const ref = yield* SubscriptionRef.make(10)
yield* SubscriptionRef.update(ref, (n) => n * 2)
const value = yield* SubscriptionRef.get(ref)
console.log(value)
})
update(const ref: SubscriptionRef.SubscriptionRef<number>const ref: {
value: A;
semaphore: Semaphore.Semaphore;
pubsub: PubSub.PubSub<A>;
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; <…;
}
ref, (n: numbern) => n: numbern + by: numberby),
reset: Effect.Effect<void, never, never>(property) reset: {
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;
}
reset: import SubscriptionRefSubscriptionRef.const set: <number>(self: SubscriptionRef.SubscriptionRef<number>, value: number) => Effect.Effect<void> (+1 overload)Sets the value of the SubscriptionRef, notifying all subscribers of the
change.
Example (Setting a value)
import { Effect, SubscriptionRef } from "effect"
const program = Effect.gen(function*() {
const ref = yield* SubscriptionRef.make(0)
yield* SubscriptionRef.set(ref, 42)
const value = yield* SubscriptionRef.get(ref)
console.log(value)
})
set(const ref: SubscriptionRef.SubscriptionRef<number>const ref: {
value: A;
semaphore: Semaphore.Semaphore;
pubsub: PubSub.PubSub<A>;
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; <…;
}
ref, 0),
}
})Place It In-Process
Wire Tag and Implementation with Hyperlink.layer:
const const CounterLive: anyconst CounterLive: {
build: (memoMap: MemoMap, scope: Scope) => Effect.Effect<Context<Counter | Hyperlink.Local<Counter>>, never, never>;
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; <…;
}
CounterLive = import HyperlinkHyperlink.layer(class Counterclass Counter {
key: Identifier;
Service: {
value: Hyperlink.Subscribable<number>;
increment: (payload: { by: number }) => Effect.Effect<void, never, never>;
reset: Effect.Effect<void, never, never>;
};
groupId: string;
description: string | undefined;
of: (this: void, self: { readonly value: Hyperlink.Subscribable<number>; readonly increment: (payload: { by: number }) => Effect.Effect<void, never, never>; readonly reset: Effect.Effect<void, never, never> }) => { readonly value: Hyperlink.Su…;
context: (self: { readonly value: Hyperlink.Subscribable<number>; readonly increment: (payload: { by: number }) => Effect.Effect<void, never, never>; readonly reset: Effect.Effect<void, never, never> }) => Context<Counter>;
use: (f: (service: { readonly value: Hyperlink.Subscribable<number>; readonly increment: (payload: { by: number }) => Effect.Effect<void, never, never>; readonly reset: Effect.Effect<void, never, never> }) => Effect.Effect<A, E, R>) => Effect.E…;
useSync: (f: (service: { readonly value: Hyperlink.Subscribable<number>; readonly increment: (payload: { by: number }) => Effect.Effect<void, never, never>; readonly reset: Effect.Effect<void, never, never> }) => A) => Effect.Effect<A, never, Count…;
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;
}
Counter, const counterImpl: Effect.Effect<
{
value: any
increment: ({
by,
}: {
readonly by: number
}) => Effect.Effect<void, never, never>
reset: Effect.Effect<void, never, never>
},
never,
never
>
const counterImpl: {
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;
}
counterImpl)Call the Handle
yield* Counter returns the Handle. Increment, read value, reset. Same shapes later sit behind RPC:
const const program: Effect.Effect<
any,
any,
any
>
const program: {
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;
}
program = import EffectEffect.const gen: <any, any>(f: () => Generator<any, any, never>) => Effect.Effect<any, unknown, unknown> (+1 overload)Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to use
Use when you want to write effectful code that looks and behaves like
synchronous code, while still handling asynchronous tasks, errors, and complex
control flow such as loops and conditions.
Generator functions work similarly to async/await but keep errors,
requirements, and interruption in the Effect type. You can yield* values
from effects and return the final result at the end.
Example (Sequencing effects with generators)
import { Data, Effect } from "effect"
class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, DiscountRateError> =>
discountRate === 0
? Effect.fail(new DiscountRateError())
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function*() {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})
gen(function* () {
const const counter: anyconst counter: {
value: Hyperlink.Subscribable<number>;
increment: (payload: { by: number }) => Effect.Effect<void, never, never>;
reset: Effect.Effect<void, never, never>;
}
counter = yield* class Counterclass Counter {
key: Identifier;
Service: {
value: Hyperlink.Subscribable<number>;
increment: (payload: { by: number }) => Effect.Effect<void, never, never>;
reset: Effect.Effect<void, never, never>;
};
groupId: string;
description: string | undefined;
of: (this: void, self: { readonly value: Hyperlink.Subscribable<number>; readonly increment: (payload: { by: number }) => Effect.Effect<void, never, never>; readonly reset: Effect.Effect<void, never, never> }) => { readonly value: Hyperlink.Su…;
context: (self: { readonly value: Hyperlink.Subscribable<number>; readonly increment: (payload: { by: number }) => Effect.Effect<void, never, never>; readonly reset: Effect.Effect<void, never, never> }) => Context<Counter>;
use: (f: (service: { readonly value: Hyperlink.Subscribable<number>; readonly increment: (payload: { by: number }) => Effect.Effect<void, never, never>; readonly reset: Effect.Effect<void, never, never> }) => Effect.Effect<A, E, R>) => Effect.E…;
useSync: (f: (service: { readonly value: Hyperlink.Subscribable<number>; readonly increment: (payload: { by: number }) => Effect.Effect<void, never, never>; readonly reset: Effect.Effect<void, never, never> }) => A) => Effect.Effect<A, never, Count…;
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;
}
Counter // counter: the Counter handle
yield* const counter: anyconst counter: {
value: Hyperlink.Subscribable<number>;
increment: (payload: { by: number }) => Effect.Effect<void, never, never>;
reset: Effect.Effect<void, never, never>;
}
counter.increment({ by: numberby: 1 })
const const n: anyn = yield* const counter: anyconst counter: {
value: Hyperlink.Subscribable<number>;
increment: (payload: { by: number }) => Effect.Effect<void, never, never>;
reset: Effect.Effect<void, never, never>;
}
counter.value.get // n: number
yield* const counter: anyconst counter: {
value: Hyperlink.Subscribable<number>;
increment: (payload: { by: number }) => Effect.Effect<void, never, never>;
reset: Effect.Effect<void, never, never>;
}
counter.reset
return const n: anyn
}).Pipeable.pipe<Effect.Effect<any, unknown, unknown>, Effect.Effect<any, any, any>>(this: Effect.Effect<any, unknown, unknown>, ab: (_: Effect.Effect<any, unknown, unknown>) => Effect.Effect<any, any, any>): Effect.Effect<any, any, any> (+21 overloads)pipe(import EffectEffect.const provide: <any>(layers: any, options?: {
readonly local?: boolean | undefined;
} | undefined) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, any, any> (+5 overloads)
Provides dependencies to an effect using layers or a context. Use options.local
to build the layer every time; by default, layers are shared between provide
calls.
Example (Providing dependencies with a layer)
import { Context, Effect, Layer } from "effect"
interface Database {
readonly query: (sql: string) => Effect.Effect<string>
}
const Database = Context.Service<Database>("Database")
const DatabaseLive = Layer.succeed(Database)({
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result for: ${sql}`))
})
const program = Effect.gen(function*() {
const db = yield* Database
return yield* db.query("SELECT * FROM users")
})
const provided = Effect.provide(program, DatabaseLive)
Effect.runPromise(provided).then(console.log)
// Output: "Result for: SELECT * FROM users"
provide(const CounterLive: anyconst CounterLive: {
build: (memoMap: MemoMap, scope: Scope) => Effect.Effect<Context<Counter | Hyperlink.Local<Counter>>, never, never>;
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; <…;
}
CounterLive))Try It Live
This exact Counter — the same Tag, the same Hyperlink.layer — is running in this page right now. The buttons call increment / reset on the Handle; the count reads straight off value.changes. There is no extra API between the UI and the resource, the Handle is the surface:
docs/CounterWhat Changes Next
Serve or client the same Tag without rewriting the program body. Only the Layer at the edge changes. That tour is Managing Layers.
Sharp edge. A browser dashboard that opens many live streams hits the browser HTTP connection cap if you pair httpServer with connect(tag, protocolHttp(port)). Serve with Node.wsServer and connect with Hyperlink.ws. Same Tag, different wire. Details live on Managing Layers.