mesh-broker's Binding is the wire form and Held carries it, so list, show and orphans answer the binding

The seat's Binding takes the fields the events carry and the membership holds
(local, storage, template, stream, subject, bucket, object-store, history,
state, indexes, snapshot, types): one form in the events, in list, show and
orphans. Held gains binding, present on an entry the bus holds. Definition,
fixture and generated Go and TypeScript contract.

Builds: design 25 bindings-first-view §2, §7 (hq d0760629, PR 543)

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-11 10:14:55 +01:00
parent f5d76e84ca
commit ed12ffb387
6 changed files with 163 additions and 25 deletions
+25 -7
View File
@@ -72,16 +72,32 @@ const SubjectRecreated = "mesh.seat.mesh-broker.event.recreated"
// SubjectDropped is where dropped is published.
const SubjectDropped = "mesh.seat.mesh-broker.event.dropped"
// Binding is the binding a module receives in its membership for one entry (ADR 0595 §1, §7).
// Binding is the binding a module receives in its membership for one entry (ADR 0595 §1, §7): the names on the bus of the entry's objects, one form in the events, in list, show and orphans, and in the membership.
type Binding struct {
// the entry's name on the bus
BusName string `json:"bus-name"`
// how the bus keeps it
StorageType string `json:"storage-type"`
// its subjects, for a stream
Subjects []string `json:"subjects,omitempty"`
// a key-value bucket's name
Bucket string `json:"bucket,omitempty"`
// a ledger's history stream
History string `json:"history,omitempty"`
// a ledger's index buckets, by index name
Indexes map[string]string `json:"indexes,omitempty"`
// the entry's local name in the module's manifest
Local string `json:"local"`
// an object store's name
ObjectStore string `json:"object-store,omitempty"`
// the subject of a ledger's instances' snapshots
Snapshot string `json:"snapshot,omitempty"`
// a ledger's state bucket
State string `json:"state,omitempty"`
// how the bus keeps it: the entry's storage type
Storage string `json:"storage"`
// a stream's name on the bus
Stream string `json:"stream,omitempty"`
// the subject a stream's messages are on; also a ledger's history's
Subject string `json:"subject,omitempty"`
// its template
Template string `json:"template,omitempty"`
// a ledger's types, as its manifest declares them
Types []map[string]any `json:"types,omitempty"`
}
// Entry is one piece of bus state as a manifest asks it.
@@ -131,6 +147,8 @@ type Fill struct {
// Held is one entry as the bus holds it.
type Held struct {
// the binding a module receives for the entry, as created and recreated carry it; present on an entry whose objects the bus holds, absent on one it does not (design 25 bindings-first-view §2)
Binding *Binding `json:"binding,omitempty"`
// its name on the bus, <module>_<local name>
BusName string `json:"bus-name"`
// its own configuration, where no template fits
+4
View File
@@ -19,6 +19,8 @@ func TestFixtures(t *testing.T) {
}{
{name: "created on the seat's subject", subject: "mesh.seat.mesh-broker.event.created", given: "{\"asked\":{\"max-mib\":64},\"bus-name\":\"mesh-knowledge_ledger\",\"fill\":{\"bytes\":2048,\"cap\":67108864,\"count\":10},\"held\":{\"max-mib\":64},\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"storage-type\":\"ledger\",\"template\":\"ledger\"}", wire: "{\"asked\":{\"max-mib\":64},\"bus-name\":\"mesh-knowledge_ledger\",\"fill\":{\"bytes\":2048,\"cap\":67108864,\"count\":10},\"held\":{\"max-mib\":64},\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"storage-type\":\"ledger\",\"template\":\"ledger\"}",
decode: func(b []byte) (any, error) { var v CreatedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-broker\\.event\\.created$")},
{name: "created with a ledger's binding as the bus module writes it", subject: "mesh.seat.mesh-broker.event.created", given: "{\"binding\":{\"history\":\"mesh-knowledge_ledger\",\"indexes\":{\"by-kind\":\"mesh-knowledge_ledger_state_index-by-kind\"},\"local\":\"ledger\",\"snapshot\":\"mesh.bus.mesh-knowledge.ledger-snapshot\",\"state\":\"mesh-knowledge_ledger_state\",\"storage\":\"ledger\",\"subject\":\"mesh.bus.mesh-knowledge.ledger\",\"template\":\"ledger\"},\"bus-name\":\"mesh-knowledge_ledger\",\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"revision\":7,\"storage-type\":\"ledger\",\"template\":\"ledger\"}", wire: "{\"binding\":{\"history\":\"mesh-knowledge_ledger\",\"indexes\":{\"by-kind\":\"mesh-knowledge_ledger_state_index-by-kind\"},\"local\":\"ledger\",\"snapshot\":\"mesh.bus.mesh-knowledge.ledger-snapshot\",\"state\":\"mesh-knowledge_ledger_state\",\"storage\":\"ledger\",\"subject\":\"mesh.bus.mesh-knowledge.ledger\",\"template\":\"ledger\"},\"bus-name\":\"mesh-knowledge_ledger\",\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"revision\":7,\"storage-type\":\"ledger\",\"template\":\"ledger\"}",
decode: func(b []byte) (any, error) { var v CreatedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-broker\\.event\\.created$")},
{name: "refused on the seat's subject", subject: "mesh.seat.mesh-broker.event.refused", given: "{\"asked\":{\"max-mib\":64},\"bus-name\":\"mesh-knowledge_ledger\",\"fill\":{\"bytes\":2048,\"cap\":67108864,\"count\":10},\"held\":{\"max-mib\":64},\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"storage-type\":\"ledger\",\"template\":\"ledger\",\"why\":\"the bus refused it\"}", wire: "{\"asked\":{\"max-mib\":64},\"bus-name\":\"mesh-knowledge_ledger\",\"fill\":{\"bytes\":2048,\"cap\":67108864,\"count\":10},\"held\":{\"max-mib\":64},\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"storage-type\":\"ledger\",\"template\":\"ledger\",\"why\":\"the bus refused it\"}",
decode: func(b []byte) (any, error) { var v RefusedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-broker\\.event\\.refused$")},
{name: "drifted on the seat's subject", subject: "mesh.seat.mesh-broker.event.drifted", given: "{\"asked\":{\"max-mib\":64},\"bus-name\":\"mesh-knowledge_ledger\",\"fill\":{\"bytes\":2048,\"cap\":67108864,\"count\":10},\"held\":{\"max-mib\":64},\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"storage-type\":\"ledger\",\"template\":\"ledger\",\"why\":\"the bus refused it\"}", wire: "{\"asked\":{\"max-mib\":64},\"bus-name\":\"mesh-knowledge_ledger\",\"fill\":{\"bytes\":2048,\"cap\":67108864,\"count\":10},\"held\":{\"max-mib\":64},\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"storage-type\":\"ledger\",\"template\":\"ledger\",\"why\":\"the bus refused it\"}",
@@ -41,6 +43,8 @@ func TestFixtures(t *testing.T) {
decode: func(b []byte) (any, error) { var v DropInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "list answers every entry", subject: "", given: "{\"entries\":[{\"bus-name\":\"mesh-knowledge_ledger\",\"fill\":{\"bytes\":2048,\"cap\":67108864,\"count\":10},\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"options\":{\"max-mib\":64},\"settings\":{\"max-mib\":64},\"storage-type\":\"ledger\",\"template\":\"ledger\"}]}", wire: "{\"entries\":[{\"bus-name\":\"mesh-knowledge_ledger\",\"fill\":{\"bytes\":2048,\"cap\":67108864,\"count\":10},\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"options\":{\"max-mib\":64},\"settings\":{\"max-mib\":64},\"storage-type\":\"ledger\",\"template\":\"ledger\"}]}",
decode: func(b []byte) (any, error) { var v ListAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "list answers the binding of an entry the bus holds", subject: "", given: "{\"entries\":[{\"binding\":{\"history\":\"mesh-knowledge_ledger\",\"indexes\":{\"by-kind\":\"mesh-knowledge_ledger_index-by-kind\"},\"local\":\"ledger\",\"snapshot\":\"mesh-knowledge_ledger.snapshot\",\"state\":\"mesh-knowledge_ledger_state\",\"storage\":\"ledger\",\"template\":\"ledger\"},\"bus-name\":\"mesh-knowledge_ledger\",\"fill\":{\"bytes\":2048,\"cap\":67108864,\"count\":10},\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"settings\":{\"max-mib\":64},\"storage-type\":\"ledger\",\"template\":\"ledger\"}]}", wire: "{\"entries\":[{\"binding\":{\"history\":\"mesh-knowledge_ledger\",\"indexes\":{\"by-kind\":\"mesh-knowledge_ledger_index-by-kind\"},\"local\":\"ledger\",\"snapshot\":\"mesh-knowledge_ledger.snapshot\",\"state\":\"mesh-knowledge_ledger_state\",\"storage\":\"ledger\",\"template\":\"ledger\"},\"bus-name\":\"mesh-knowledge_ledger\",\"fill\":{\"bytes\":2048,\"cap\":67108864,\"count\":10},\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"settings\":{\"max-mib\":64},\"storage-type\":\"ledger\",\"template\":\"ledger\"}]}",
decode: func(b []byte) (any, error) { var v ListAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
}
for _, c := range cases {
v, err := c.decode([]byte(c.given))
+25
View File
@@ -22,6 +22,31 @@
}
}
},
{
"name": "created with a ledger's binding as the bus module writes it",
"of": "event:created",
"subject": "mesh.seat.mesh-broker.event.created",
"given": {
"module": "mesh-knowledge",
"local-name": "ledger",
"storage-type": "ledger",
"template": "ledger",
"bus-name": "mesh-knowledge_ledger",
"binding": {
"local": "ledger",
"storage": "ledger",
"template": "ledger",
"subject": "mesh.bus.mesh-knowledge.ledger",
"history": "mesh-knowledge_ledger",
"state": "mesh-knowledge_ledger_state",
"indexes": {
"by-kind": "mesh-knowledge_ledger_state_index-by-kind"
},
"snapshot": "mesh.bus.mesh-knowledge.ledger-snapshot"
},
"revision": 7
}
},
{
"name": "refused on the seat's subject",
"of": "event:refused",
+34
View File
@@ -47,5 +47,39 @@
}
]
}
},
{
"name": "list answers the binding of an entry the bus holds",
"of": "verb:list.answer",
"given": {
"entries": [
{
"module": "mesh-knowledge",
"local-name": "ledger",
"storage-type": "ledger",
"template": "ledger",
"bus-name": "mesh-knowledge_ledger",
"settings": {
"max-mib": 64
},
"fill": {
"count": 10,
"bytes": 2048,
"cap": 67108864
},
"binding": {
"local": "ledger",
"storage": "ledger",
"template": "ledger",
"history": "mesh-knowledge_ledger",
"state": "mesh-knowledge_ledger_state",
"indexes": {
"by-kind": "mesh-knowledge_ledger_index-by-kind"
},
"snapshot": "mesh-knowledge_ledger.snapshot"
}
}
]
}
}
]
+50 -11
View File
@@ -403,6 +403,10 @@
"why": {
"type": "string",
"description": "why it is in its state, in plain words; empty when held as asked"
},
"binding": {
"$ref": "#/defs/Binding",
"description": "the binding a module receives for the entry, as created and recreated carry it; present on an entry whose objects the bus holds, absent on one it does not (design 25 bindings-first-view §2)"
}
},
"required": [
@@ -540,31 +544,66 @@
"Binding": {
"type": "object",
"properties": {
"bus-name": {
"local": {
"type": "string",
"description": "the entry's name on the bus"
"description": "the entry's local name in the module's manifest"
},
"storage-type": {
"storage": {
"type": "string",
"description": "how the bus keeps it"
"description": "how the bus keeps it: the entry's storage type"
},
"template": {
"type": "string",
"description": "its template"
},
"subjects": {
"type": "array",
"items": {
"stream": {
"type": "string",
"description": "a stream's name on the bus"
},
"subject": {
"type": "string",
"description": "the subject a stream's messages are on; also a ledger's history's"
},
"bucket": {
"type": "string",
"description": "a key-value bucket's name"
},
"object-store": {
"type": "string",
"description": "an object store's name"
},
"history": {
"type": "string",
"description": "a ledger's history stream"
},
"state": {
"type": "string",
"description": "a ledger's state bucket"
},
"indexes": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "its subjects, for a stream"
"description": "a ledger's index buckets, by index name"
},
"snapshot": {
"type": "string",
"description": "the subject of a ledger's instances' snapshots"
},
"types": {
"type": "array",
"items": {
"type": "object"
},
"description": "a ledger's types, as its manifest declares them"
}
},
"required": [
"bus-name",
"storage-type"
"local",
"storage"
],
"description": "the binding a module receives in its membership for one entry (ADR 0595 §1, §7)"
"description": "the binding a module receives in its membership for one entry (ADR 0595 §1, §7): the names on the bus of the entry's objects, one form in the events, in list, show and orphans, and in the membership"
}
}
}
+25 -7
View File
@@ -47,16 +47,32 @@ export const recreatedSubject = "mesh.seat.mesh-broker.event.recreated";
export const droppedSubject = "mesh.seat.mesh-broker.event.dropped";
/** the binding a module receives in its membership for one entry (ADR 0595 §1, §7) */
/** the binding a module receives in its membership for one entry (ADR 0595 §1, §7): the names on the bus of the entry's objects, one form in the events, in list, show and orphans, and in the membership */
export interface Binding {
/** the entry's name on the bus */
"bus-name": string;
/** how the bus keeps it */
"storage-type": string;
/** its subjects, for a stream */
"subjects"?: string[];
/** a key-value bucket's name */
"bucket"?: string;
/** a ledger's history stream */
"history"?: string;
/** a ledger's index buckets, by index name */
"indexes"?: Record<string, string>;
/** the entry's local name in the module's manifest */
"local": string;
/** an object store's name */
"object-store"?: string;
/** the subject of a ledger's instances' snapshots */
"snapshot"?: string;
/** a ledger's state bucket */
"state"?: string;
/** how the bus keeps it: the entry's storage type */
"storage": string;
/** a stream's name on the bus */
"stream"?: string;
/** the subject a stream's messages are on; also a ledger's history's */
"subject"?: string;
/** its template */
"template"?: string;
/** a ledger's types, as its manifest declares them */
"types"?: Record<string, unknown>[];
}
/** one piece of bus state as a manifest asks it */
@@ -86,6 +102,8 @@ export interface Fill {
/** one entry as the bus holds it */
export interface Held {
/** the binding a module receives for the entry, as created and recreated carry it; present on an entry whose objects the bus holds, absent on one it does not (design 25 bindings-first-view §2) */
"binding"?: Binding;
/** its name on the bus, <module>_<local name> */
"bus-name": string;
/** its own configuration, where no template fits */