mesh-delivery-contracts: the client of mesh-delivery (heads hash, merge ask key, ask id, owed acts and keys, owed values) and the checker client's preview rules (D-27, ADR 0625 §4, ADR 0635 §5; issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 14:47:16 +01:00
parent dcd1f3c22b
commit ccef33ef31
7 changed files with 562 additions and 0 deletions
@@ -6,6 +6,7 @@ package meshdeliverycheckerclient
import (
"fmt"
pathpkg "path"
"strings"
checker "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker"
@@ -220,3 +221,60 @@ func CheckKept(k checker.KeptByLevelDigestValue) error {
}
return nil
}
// CheckPreview refuses a preview a reader could not place (the checker writes it, the indexer reads it for the candidates'
// fingerprints): a repository, head or trunk commit that does not key, a head in conflict that names no file or has a merge
// commit or modules, a head that merged and names no merge commit, and a module with no directory, no tree, a manifest
// digest that is not 64 lowercase hex digits, a lock without path or hash, or a named path that is not clean and inside the
// module's directory or names no git object.
func CheckPreview(p checker.PreviewsByRepositoryHeadTrunkValue) error {
const who = "meshdeliverycheckerclient: "
if err := CheckRepository(p.Repository); err != nil {
return err
}
if err := keys.CheckName("head", p.Head); err != nil {
return fmt.Errorf(who+"the preview: %w", err)
}
if err := keys.CheckName("trunk commit", p.Trunk); err != nil {
return fmt.Errorf(who+"the preview: %w", err)
}
for _, f := range p.Conflicts {
if f == "" {
return fmt.Errorf(who + "a preview names a conflicting file with an empty path")
}
}
if len(p.Conflicts) > 0 {
if p.Merge != "" || len(p.Modules) > 0 {
return fmt.Errorf(who + "a preview of a head in conflict has no merge commit and no modules")
}
return nil
}
if p.Merge == "" {
return fmt.Errorf(who + "a preview names no merge commit")
}
for dir, m := range p.Modules {
if dir == "" {
return fmt.Errorf(who + "a preview names a module with an empty directory")
}
if m.Tree == "" {
return fmt.Errorf(who+"the preview's module %q has no tree", dir)
}
if !keys.IsHex64(m.Manifest) {
return fmt.Errorf(who+"the preview's module %q: %q is not a manifest digest, 64 lowercase hex digits", dir, m.Manifest)
}
for path, hash := range m.Locks {
if path == "" || hash == "" {
return fmt.Errorf(who+"the lock %q of the preview's module %q has an empty path or hash", path, dir)
}
}
for p, object := range m.Paths {
if p == "" || p != pathpkg.Clean(p) || strings.HasPrefix(p, "/") || p == ".." || strings.HasPrefix(p, "../") {
return fmt.Errorf(who+"the preview's module %q: the named path %q is not a clean path inside the module's directory", dir, p)
}
if object == "" {
return fmt.Errorf(who+"the preview's module %q: the named path %q names no git object", dir, p)
}
}
}
return nil
}
@@ -59,6 +59,7 @@ func TestAVerdictIsRefusedForWhatAReaderCouldNotPlace(t *testing.T) {
}
func TestAKeptVerdictIsAPassOfAPackageAtALevel(t *testing.T) {
t.Run("a preview is refused for what a reader of candidates could not place", previewIsRefused)
ok := checker.KeptByLevelDigestValue{State: VerdictPass, Repository: "novox/mesh-sdk", Package: "./go/keys", Level: LevelUnit, Toolchain: "1.26.0", At: "2026-11-05T10:00:00Z"}
if err := CheckKept(ok); err != nil {
t.Fatalf("a kept pass is refused: %v", err)
@@ -79,3 +80,48 @@ func TestAKeptVerdictIsAPassOfAPackageAtALevel(t *testing.T) {
}
}
}
// A preview is refused for what a reader of candidates could not place.
func previewIsRefused(t *testing.T) {
good := func() checker.PreviewsByRepositoryHeadTrunkValue {
return checker.PreviewsByRepositoryHeadTrunkValue{Repository: "novox/a", Head: "h1", Trunk: "t1", Merge: "m1",
Modules: map[string]checker.PreviewsByRepositoryHeadTrunkValueModulesValue{"mod": {Tree: "tr", Manifest: strings.Repeat("a", 64),
Locks: map[string]string{"go.sum": "x"}, Paths: map[string]string{"src": "o1"}}}}
}
if err := CheckPreview(good()); err != nil {
t.Fatalf("a good preview is refused: %v", err)
}
for name, c := range map[string]struct {
edit func(*checker.PreviewsByRepositoryHeadTrunkValue)
says string
}{
"a repository with no owner": {func(p *checker.PreviewsByRepositoryHeadTrunkValue) { p.Repository = "a" }, "<owner>/<name>"},
"a head with a dot": {func(p *checker.PreviewsByRepositoryHeadTrunkValue) { p.Head = "h.1" }, "head"},
"a conflict with a merge": {func(p *checker.PreviewsByRepositoryHeadTrunkValue) { p.Conflicts = []string{"f"} }, "in conflict"},
"no merge commit": {func(p *checker.PreviewsByRepositoryHeadTrunkValue) { p.Merge = "" }, "no merge commit"},
"a module with no tree": {func(p *checker.PreviewsByRepositoryHeadTrunkValue) {
m := p.Modules["mod"]
m.Tree = ""
p.Modules["mod"] = m
}, "no tree"},
"a short manifest digest": {func(p *checker.PreviewsByRepositoryHeadTrunkValue) {
m := p.Modules["mod"]
m.Manifest = "abc"
p.Modules["mod"] = m
}, "manifest digest"},
"a path outside the module": {func(p *checker.PreviewsByRepositoryHeadTrunkValue) {
m := p.Modules["mod"]
m.Paths = map[string]string{"../x": "o"}
p.Modules["mod"] = m
}, "clean path"},
} {
p := good()
c.edit(&p)
if err := CheckPreview(p); err == nil || !strings.Contains(err.Error(), c.says) {
t.Errorf("%s: CheckPreview = %v, want it to say %q", name, err, c.says)
}
}
if err := CheckPreview(checker.PreviewsByRepositoryHeadTrunkValue{Repository: "novox/a", Head: "h", Trunk: "t", Conflicts: []string{"f"}}); err != nil {
t.Errorf("a head in conflict is refused: %v", err)
}
}
@@ -0,0 +1,323 @@
// Package meshdeliveryclient holds the logic the holders and callers of the seat mesh-delivery share over its contract
// (novox/hq ADR 0625 §4, ADR 0634 §2, ADR 0635 §5): the keys of its states `merges`, `asks` and `owed` and the rules their
// values keep, the id the teller gives the router for an ask, and the act an ask's options bind. The gate writes them, the
// forge bridge, the teller and the assigner read them; the contract's decoding is strict by schema only, so these rules are
// written once here and no caller copies them.
package meshdeliveryclient
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"regexp"
"sort"
"strconv"
"strings"
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery"
"git.novox.be/novox/mesh-sdk/go/keys"
)
// AskKindMerge is the kind of the gate's ask for a group's merge, the first part of its key.
const AskKindMerge = "merge"
// VerbMerge is the act an ask for a group's merge binds in its options, `mesh-delivery.merge` (Approve).
const VerbMerge = "merge"
// Head is one pull request at one commit: the repository as `<owner>/<name>` and the full sha.
type Head struct {
Repository string `json:"repository"`
Number int `json:"number"`
Head string `json:"head"`
}
// HeadsHash is the first 12 hex digits of the sha256 over the lines `<repository>#<number>=<head>`, sorted and joined by a
// newline: the same for every order of heads, different when any head changes. An empty list is refused.
func HeadsHash(heads []Head) (string, error) {
if len(heads) == 0 {
return "", fmt.Errorf("meshdeliveryclient: a heads hash needs at least one pull request")
}
lines := make([]string, len(heads))
for i, h := range heads {
lines[i] = fmt.Sprintf("%s#%d=%s", h.Repository, h.Number, h.Head)
}
sort.Strings(lines)
sum := sha256.Sum256([]byte(strings.Join(lines, "\n")))
return hex.EncodeToString(sum[:])[:12], nil
}
var hashOK = regexp.MustCompile(`^[0-9a-f]{12}$`)
func groupKey(group string) (string, error) {
if group == "" {
return "", fmt.Errorf("meshdeliveryclient: the group name is empty")
}
return keys.EscapeName(group), nil
}
// MergesKey is the key, in the state merges, of a group: `<group>`, escaped so a branch such as `fix/1.2` keys.
func MergesKey(group string) (string, error) { return groupKey(group) }
// MergeAskKey is the key, in the state asks, of the ask for merging a group at these heads: `merge.<group>.<heads hash>`.
// The teller's answer has the same key.
func MergeAskKey(group string, heads []Head) (string, error) {
part, err := groupKey(group)
if err != nil {
return "", err
}
hash, err := HeadsHash(heads)
if err != nil {
return "", err
}
return AskKindMerge + "." + part + "." + hash, nil
}
// ParseAskKey is the inverse of MergeAskKey: the kind, the group and the heads hash.
func ParseAskKey(key string) (kind, group, hash string, err error) {
parts := strings.Split(key, ".")
if len(parts) != 3 || parts[0] != AskKindMerge {
return "", "", "", fmt.Errorf("meshdeliveryclient: %q is not an ask key, merge.<group>.<heads hash>", key)
}
if parts[1] == "" {
return "", "", "", fmt.Errorf("meshdeliveryclient: %q is not an ask key: the group name is empty", key)
}
if group, err = keys.UnescapeName(parts[1]); err != nil {
return "", "", "", fmt.Errorf("meshdeliveryclient: %q is not an ask key: %w", key, err)
}
if !hashOK.MatchString(parts[2]) {
return "", "", "", fmt.Errorf("meshdeliveryclient: %q is not an ask key: %q is not 12 hex digits", key, parts[2])
}
return parts[0], group, parts[2], nil
}
// SettleGroupPrefix begins the group of a settle ask (novox/hq ADR 0556 clause 5): a person's ask to settle deliveries held
// because their merge reached the trunk without a passing check. It is kept in the asks under MergeAskKey like a merge ask,
// so the teller carries it unchanged. Git forbids a colon in a branch name, so no pull request's group ever begins with it.
const SettleGroupPrefix = "settle:"
// SettleGroup is the group of the settle ask of these delivery ids: the prefix and twelve hex digits of the sha256 over the
// ids, sorted and joined by a newline, so the same deliveries asked again keep one key.
func SettleGroup(ids []string) string {
sorted := append([]string(nil), ids...)
sort.Strings(sorted)
sum := sha256.Sum256([]byte(strings.Join(sorted, "\n")))
return SettleGroupPrefix + hex.EncodeToString(sum[:])[:12]
}
// AskVerb is the act the options of an ask bind, by its group: a settle ask's `mesh-delivery.settle`, any other
// `mesh-delivery.merge`. The gate composes the act with it and the teller checks a warrant against it.
func AskVerb(group string) string {
if strings.HasPrefix(group, SettleGroupPrefix) {
return seat.Seat + "." + seat.VerbSettle
}
return seat.Seat + "." + VerbMerge
}
// AskID is the id the teller gives the router for the ask at askKey. The router's ids refuse dots, so in the readable part a
// dot becomes `-` and any other byte outside letters, digits, `_` and `-` becomes `_`; that part is cut to fit, and every id
// ends in `-` and the first 12 hex digits of the sha256 of the whole key, so two keys never give one id. It cannot be read
// back: the teller keeps the key beside it.
func AskID(askKey string) string {
const most = 64 // the router's bound on an id
const tail = 13 // "-" and 12 hex digits
readable := []byte(askKey)
for i, c := range readable {
switch {
case c >= 'A' && c <= 'Z', c >= 'a' && c <= 'z', c >= '0' && c <= '9':
case c == '.':
readable[i] = '-'
default: // a slash, an equals sign or any other byte the router's id refuses
readable[i] = '_'
}
}
if len(readable) > 0 && (readable[0] == '-' || readable[0] == '_') {
readable[0] = 'k' // an id starts with a letter or digit
}
if len(readable) > most-tail {
readable = readable[:most-tail]
}
sum := sha256.Sum256([]byte(askKey))
return string(readable) + "-" + hex.EncodeToString(sum[:])[:12]
}
// AskingID is the id of the asking-th writing of the ask at askKey. Asking 0 and 1 give AskID(askKey), so the first asking's
// id is unchanged; asking n of 2 or more gives the AskID form of askKey + "." + n, so each asking has an id of its own and the
// router, which drops an ask under an id it keeps as a redelivery, takes each as new and an answer to an earlier asking is
// never taken for a later one.
func AskingID(askKey string, asking int) string {
if asking < 2 {
return AskID(askKey)
}
return AskID(askKey + "." + strconv.Itoa(asking))
}
// The acts the gate owes the forge, the last part of an owed key.
const (
OwedStatus = "status"
OwedView = "view"
OwedNote = "note"
OwedComment = "comment"
)
// The status contexts the mesh owes the forge.
const (
StatusContextMergeGate = "mesh/merge-gate"
StatusContextDelivery = "mesh/delivery"
StatusContextDeliveryGroup = "mesh/delivery-group"
StatusContextRepoCheck = "mesh/repo-check"
StatusContextCheck = "mesh/check"
)
var statusContexts = []string{StatusContextMergeGate, StatusContextDelivery, StatusContextDeliveryGroup, StatusContextRepoCheck, StatusContextCheck}
// StatusContexts is the contexts, in a new slice.
func StatusContexts() []string { return append([]string(nil), statusContexts...) }
// The states a status the gate owes the forge may carry.
var statusStates = []string{"pending", "success", "failure", "error", "warning"}
func checkStatusContext(context string) error {
for _, c := range statusContexts {
if context == c {
return nil
}
}
return fmt.Errorf("meshdeliveryclient: the status context %q is not one of %s", context, strings.Join(statusContexts, ", "))
}
// ParseOwedAct splits an owed act into its kind and, for a status, its context, for a comment its sequence as digits. The act
// is `status=<context>`, `view`, `note` or `comment.<seq>`.
func ParseOwedAct(act string) (kind, context string, err error) {
kind, context, hasContext := strings.Cut(act, "=")
switch kind {
case OwedStatus:
if !hasContext {
return "", "", fmt.Errorf("meshdeliveryclient: the owed act %q is a status without its context, status=<context>", act)
}
if err := checkStatusContext(context); err != nil {
return "", "", err
}
return kind, context, nil
case OwedView, OwedNote:
if hasContext {
return "", "", fmt.Errorf("meshdeliveryclient: the owed act %q takes no context", act)
}
return kind, "", nil
}
if kind, seq, ok := strings.Cut(act, "."); ok && kind == OwedComment {
if n, err := strconv.Atoi(seq); err != nil || n < 1 || strconv.Itoa(n) != seq {
return "", "", fmt.Errorf("meshdeliveryclient: the owed act %q is a comment without its sequence, comment.<seq> with seq of 1 or more", act)
}
return OwedComment, seq, nil
}
return "", "", fmt.Errorf("meshdeliveryclient: the owed act %q is not status=<context>, view, note or comment.<seq>", act)
}
// OwedCommentAct is the act of the seq-th comment on a head: `comment.<seq>`, seq of 1 or more, so a second comment has a key
// of its own and does not overwrite the first.
func OwedCommentAct(seq int) (string, error) {
if seq < 1 {
return "", fmt.Errorf("meshdeliveryclient: a comment's sequence is 1 or more, not %d", seq)
}
return OwedComment + "." + strconv.Itoa(seq), nil
}
func escapeRepository(repository string) (string, error) {
owner, name, ok := strings.Cut(repository, "/")
if !ok || owner == "" || name == "" || strings.Contains(name, "/") {
return "", fmt.Errorf("meshdeliveryclient: the repository %q is not <owner>/<name>, with exactly one slash", repository)
}
return keys.EscapeName(owner) + "/" + keys.EscapeName(name), nil
}
func unescapeRepository(part string) (string, error) {
owner, name, ok := strings.Cut(part, "/")
if !ok || owner == "" || name == "" || strings.Contains(name, "/") {
return "", fmt.Errorf("meshdeliveryclient: the repository %q is not <owner>/<name>, with exactly one slash", part)
}
o, err := keys.UnescapeName(owner)
if err != nil {
return "", err
}
n, err := keys.UnescapeName(name)
if err != nil {
return "", err
}
return o + "/" + n, nil
}
// OwedKey is the key, in the state owed, of an act the gate owes the forge on a pull request at a head:
// `<repository>.<number>.<head>.<act>`. The number is 0 for a commit no pull request names, and never negative.
func OwedKey(repository string, number int, head, act string) (string, error) {
repo, err := escapeRepository(repository)
if err != nil {
return "", err
}
if number < 0 {
return "", fmt.Errorf("meshdeliveryclient: an owed key's pull request number is 0 (none) or more, not %d", number)
}
if err := keys.CheckName("head", head); err != nil {
return "", err
}
if _, _, err := ParseOwedAct(act); err != nil {
return "", err
}
return repo + "." + strconv.Itoa(number) + "." + head + "." + act, nil
}
// ParseOwedKey is the inverse of OwedKey; act is whole, as `status=<context>` for a status.
func ParseOwedKey(key string) (repository string, number int, head, act string, err error) {
parts := strings.SplitN(key, ".", 4) // a comment's act holds a dot
if len(parts) != 4 {
return "", 0, "", "", fmt.Errorf("meshdeliveryclient: %q is not an owed key, <repository>.<number>.<head>.<act>", key)
}
number, convErr := strconv.Atoi(parts[1])
if convErr != nil {
return "", 0, "", "", fmt.Errorf("meshdeliveryclient: %q is not an owed key: %q is not a pull request number", key, parts[1])
}
if repository, err = unescapeRepository(parts[0]); err != nil {
return "", 0, "", "", fmt.Errorf("meshdeliveryclient: %q is not an owed key: %w", key, err)
}
if _, err := OwedKey(repository, number, parts[2], parts[3]); err != nil {
return "", 0, "", "", fmt.Errorf("meshdeliveryclient: %q is not an owed key: %w", key, err)
}
return repository, number, parts[2], parts[3], nil
}
// CheckOwed refuses an owed value the forge bridge could not perform: a status needs its context (one the mesh owes), a
// state of the list and a target; a view and a comment need text; a note needs text and a ref.
func CheckOwed(o seat.OwedByRepositoryNumberHeadActValue) error {
const who = "meshdeliveryclient: "
switch o.Act {
case OwedStatus:
if o.Context == "" {
return fmt.Errorf(who + "an owed status has no context")
}
if err := checkStatusContext(o.Context); err != nil {
return err
}
if o.Target == "" {
return fmt.Errorf(who + "an owed status has no target")
}
for _, s := range statusStates {
if o.State == s {
return nil
}
}
return fmt.Errorf(who+"the owed status state %q is not %s", o.State, strings.Join(statusStates, ", "))
case OwedView, OwedComment:
if o.Text == "" {
return fmt.Errorf(who+"an owed %s has no text", o.Act)
}
case OwedNote:
if o.Text == "" {
return fmt.Errorf(who + "an owed note has no text")
}
if o.Ref == "" {
return fmt.Errorf(who + "an owed note has no ref")
}
default:
return fmt.Errorf(who+"the owed act %q is not status, view, note or comment", o.Act)
}
return nil
}
@@ -0,0 +1,91 @@
package meshdeliveryclient
import (
"strings"
"testing"
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery"
)
// The keys, ids and acts equal what the gate and the teller have always written (the expected strings are the trunk's own
// output for the same input), so a value written before is read after.
func TestTheAsksAndOwedActsKeepTheirFormsAndRules(t *testing.T) {
heads := []Head{{Repository: "novox/a", Number: 3, Head: "abc"}, {Repository: "novox/b", Number: 1, Head: "def"}}
key, err := MergeAskKey("fix/1.2", heads)
if err != nil || key != "merge.fix/1=2e2.d106c7950d28" {
t.Fatalf("MergeAskKey = %q, %v", key, err)
}
if rev, _ := MergeAskKey("fix/1.2", []Head{heads[1], heads[0]}); rev != key {
t.Errorf("the heads' order changes the key: %q", rev)
}
if kind, group, hash, err := ParseAskKey(key); err != nil || kind != "merge" || group != "fix/1.2" || hash != "d106c7950d28" {
t.Errorf("ParseAskKey = %q %q %q %v", kind, group, hash, err)
}
for _, bad := range []string{"merge.g", "settle.g.d106c7950d28", "merge..d106c7950d28", "merge.g.D106C7950D28", "merge.g.d106"} {
if _, _, _, err := ParseAskKey(bad); err == nil {
t.Errorf("ParseAskKey(%q) reads a wrong key", bad)
}
}
if _, err := MergeAskKey("", heads); err == nil {
t.Error("an empty group keys")
}
if _, err := MergeAskKey("g", nil); err == nil {
t.Error("no heads keys")
}
if id := AskID(key); id != "merge-fix_1_2e2-d106c7950d28-400030402c88" || AskingID(key, 1) != id || AskingID(key, 0) != id {
t.Errorf("AskID = %q", id)
}
if id := AskingID(key, 3); id != "merge-fix_1_2e2-d106c7950d28-3-ea06879e48d8" || len(id) > 64 {
t.Errorf("AskingID(3) = %q", id)
}
if g := SettleGroup([]string{"b", "a"}); g != "settle:7e18f737311b" || g != SettleGroup([]string{"a", "b"}) {
t.Errorf("SettleGroup = %q", g)
}
if AskVerb(SettleGroup([]string{"a"})) != "mesh-delivery.settle" || AskVerb("fix/1") != "mesh-delivery.merge" {
t.Error("AskVerb binds the wrong act")
}
for act, want := range map[string]string{"status=mesh/check": "novox/a.3.abc.status=mesh/check", "view": "novox/a.3.abc.view", "comment.2": "novox/a.3.abc.comment.2"} {
k, err := OwedKey("novox/a", 3, "abc", act)
if err != nil || k != want {
t.Errorf("OwedKey(%q) = %q, %v", act, k, err)
}
if repo, n, head, a, err := ParseOwedKey(k); err != nil || repo != "novox/a" || n != 3 || head != "abc" || a != act {
t.Errorf("ParseOwedKey(%q) = %q %d %q %q %v", k, repo, n, head, a, err)
}
}
for _, act := range []string{"status", "status=mesh/other", "view=x", "comment", "comment.0", "comment.01", "other"} {
if _, err := OwedKey("novox/a", 3, "abc", act); err == nil {
t.Errorf("OwedKey takes the act %q", act)
}
}
if _, err := OwedKey("novox", 0, "abc", "view"); err == nil {
t.Error("a repository with no slash keys")
}
if _, err := OwedKey("novox/a", -1, "abc", "view"); err == nil {
t.Error("a negative number keys")
}
if act, err := OwedCommentAct(2); err != nil || act != "comment.2" {
t.Errorf("OwedCommentAct = %q, %v", act, err)
}
type owed = seat.OwedByRepositoryNumberHeadActValue
for name, c := range map[string]struct {
v owed
says string
}{
"a good status": {owed{Act: "status", Context: StatusContextCheck, Target: "http://x", State: "success"}, ""},
"a good note": {owed{Act: "note", Text: "t", Ref: "mesh-plan"}, ""},
"a status no target": {owed{Act: "status", Context: StatusContextCheck, State: "success"}, "no target"},
"a status wrong state": {owed{Act: "status", Context: StatusContextCheck, Target: "x", State: "done"}, "state"},
"a status no context": {owed{Act: "status", Target: "x", State: "success"}, "no context"},
"a view no text": {owed{Act: "view"}, "no text"},
"a note no ref": {owed{Act: "note", Text: "t"}, "no ref"},
"an unknown act": {owed{Act: "merge"}, "not status"},
} {
err := CheckOwed(c.v)
if (c.says == "") != (err == nil) || (err != nil && !strings.Contains(err.Error(), c.says)) {
t.Errorf("%s: CheckOwed = %v, want %q", name, err, c.says)
}
}
}
@@ -0,0 +1,17 @@
module git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient
go 1.26.0
require (
git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery v0.0.0
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef
)
require (
github.com/klauspost/compress v1.20.1 // indirect
github.com/nats-io/nats.go v1.54.0 // indirect
github.com/nats-io/nkeys v0.4.16 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
golang.org/x/crypto v0.57.0 // indirect
golang.org/x/sys v0.48.0 // indirect
)
@@ -0,0 +1,18 @@
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104172218-347a3053580a h1:E9ERPqR4uZXypudHGxv2/wNIOYcder9torVqJwJq/DI=
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104172218-347a3053580a/go.mod h1:0dD1XEJBJ0G5UYDQxSZI84nuujb6m3tOpJl4C8rQVvg=
git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0-20261105000000-000000000000 h1:BTqpa7dCCTQH9m08Jle8KVFwyIHiHPSUbwWxuqSg2yA=
git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0-20261105000000-000000000000/go.mod h1:pPxwhTMb3hIk7UUrBicJnGQCqfTEoBGJvNFspEK2U18=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef h1:/Jic5ho0xz32Z9DH2NSUqVxCF1KyfIOWuRTrX1aLjus=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef/go.mod h1:2wHG7ntLCG8Tpm/SCfgp524a5emUJm1gAC4KrRYsU9s=
github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ=
github.com/klauspost/compress v1.20.1/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
github.com/nats-io/nats.go v1.54.0 h1:vsXoOxjHp/GmPUN+EcI7uOf/uB+iAP+kEsAFNQN0yzA=
github.com/nats-io/nats.go v1.54.0/go.mod h1:y+DZoD1oBOYfZTU681eTUiUjI0vbqYGixNVFHcjHJ0k=
github.com/nats-io/nkeys v0.4.16 h1:rd5oAuLOb8mnAycB0xleuEBNS1pVVnN0fv/FF34Eypg=
github.com/nats-io/nkeys v0.4.16/go.mod h1:llLgWoI0o4z/Q57q2R1kHfmocyhGV6VG/U18Glg1Afs=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
+9
View File
@@ -121,6 +121,15 @@
],
"series": "0.1"
},
{
"name": "go-meshdeliveryclient",
"kind": "package",
"language": "go",
"paths": [
"go/clients/meshdeliveryclient"
],
"series": "0.1"
},
{
"name": "go-meshdeliverycheckerclient",
"kind": "package",