A node that reports file-references gets the digest and size of each file over 64 KiB that may travel so, and the content is put in the bucket declaration-files before the declaration is sent; others get every content inline. The controller alone writes the bucket and node-engines read an object by name (the four grants the node-engine's own fetch makes, no list, no write). An object goes a day after no node's latest declaration names it. The near-limit condition warns at 75 percent, and a test holds the control node's declaration, in the reference form, under half the bus's largest message.
97 lines
3.6 KiB
Go
97 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/novox/mesh-controller/internal/conditions"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
// **A declaration is one bus message, and the bus's largest message is a cliff** (novox/hq issue 1689). On 2026-10-21
|
|
// the control node's declaration passed it by 2 KB and nothing reached that node until two assignments were undone.
|
|
// So the size is measured at every send: a warning condition from 75% of the limit (ADR 0486 §8, issue 1698: it was 90% while every file travelled inline), urgent over it, cleared when
|
|
// the declaration is back under 75%. The limit is the connection's own, not a constant.
|
|
|
|
// kindDeclarationNearLimit is a declaration at or near the bus's largest message.
|
|
const kindDeclarationNearLimit = "declaration-near-limit"
|
|
|
|
// declarationRoomSource is the condition's source: the sends.
|
|
const declarationRoomSource = "sends"
|
|
|
|
func declarationRoomObservation(node string, size, limit int64) conditions.Observation {
|
|
o := conditions.Observation{Scope: conditions.ScopeMachine, ID: node, Kind: kindDeclarationNearLimit,
|
|
Token: kindDeclarationNearLimit, Machine: node, Severity: conditions.Warning, Source: declarationRoomSource}
|
|
if size > limit {
|
|
o.Severity = conditions.Urgent
|
|
}
|
|
o.Summary = fmt.Sprintf("%s's declaration is %d bytes on the wire, %d%% of the bus's largest message of %d bytes",
|
|
node, size, size*100/limit, limit)
|
|
if size > limit {
|
|
o.Summary += ": over it, so it cannot be sent"
|
|
}
|
|
return o
|
|
}
|
|
|
|
func init() {
|
|
link.DeclarationMeasured = func(ctx context.Context, node string, size, limit int64) {
|
|
settleDeclarationRoom(ctx, conditionsFrom, node, size, limit)
|
|
}
|
|
}
|
|
|
|
// settleDeclarationRoom raises or clears the condition of a declaration's size. A nil keeper (a tool, not the
|
|
// serving controller) says nothing.
|
|
func settleDeclarationRoom(ctx context.Context, keeper *conditions.Keeper, node string, size, limit int64) {
|
|
if keeper == nil || limit <= 0 {
|
|
return
|
|
}
|
|
obs := declarationRoomObservation(node, size, limit)
|
|
if float64(size) < link.NearTheLimit*float64(limit) {
|
|
if _, err := keeper.Clear(ctx, obs.Key(), fmt.Sprintf("%s's declaration is %d bytes, under the limit's margin", node, size)); err != nil {
|
|
fmt.Printf("the condition %s could not be cleared: %v\n", obs.Key(), err)
|
|
}
|
|
return
|
|
}
|
|
fmt.Printf("NEAR THE LIMIT: %s\n", obs.Summary)
|
|
if _, err := keeper.Observe(ctx, obs); err != nil {
|
|
fmt.Printf("the condition %s could not be raised: %v\n", obs.Key(), err)
|
|
}
|
|
}
|
|
|
|
// compactKept writes the vault's export compact. The composition (the SDK's keptFile) writes it indented, and its 334
|
|
// sealed secrets were 164,763 bytes of it against 138,474 compact: a fifth of the declaration's room, in whitespace,
|
|
// base64-encoded again on the wire. Every reader parses JSON, so the spacing is nobody's. A resource that is not
|
|
// the export, or whose content is not JSON, is left as it is. The resources are copied, never changed in place.
|
|
func compactKept(resources []map[string]any) []map[string]any {
|
|
out := make([]map[string]any, len(resources))
|
|
for i, r := range resources {
|
|
out[i] = r
|
|
id, _ := r["id"].(string)
|
|
path, _ := r["path"].(string)
|
|
content, ok := r["content"].(string)
|
|
if !ok || (id != "kept" && !strings.HasSuffix(id, ".kept")) || !strings.HasSuffix(path, "/export.json") {
|
|
continue
|
|
}
|
|
var dst bytes.Buffer
|
|
if json.Compact(&dst, []byte(content)) != nil {
|
|
continue
|
|
}
|
|
if strings.HasSuffix(content, "\n") {
|
|
dst.WriteByte('\n')
|
|
}
|
|
if dst.String() == content {
|
|
continue
|
|
}
|
|
c := make(map[string]any, len(r))
|
|
for k, v := range r {
|
|
c[k] = v
|
|
}
|
|
c["content"] = dst.String()
|
|
out[i] = c
|
|
}
|
|
return out
|
|
}
|