Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
22 changes: 22 additions & 0 deletions LANDSCAPE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# TriTRPC landscape positioning

Intended category: Agent Protocols.

Reasoning:
- stable TritRPC v1 specification
- canonical encoding contract
- cross-language parity across Rust and Go implementations
- strict fixture verification
- authenticated envelope framing

Summary tag: Open Source.

Recommended one-line description:
Deterministic, authenticated RPC protocol with canonical fixtures and cross-language parity for agent interoperability.

Suggested homepage URL: https://github.com/SocioProphet/TriTRPC
Suggested repo URL: https://github.com/SocioProphet/TriTRPC
Suggested logo filename: tritrpc.svg
Repo-local logo asset path: assets/tritrpc.svg

This file exists so the external listing work is captured in-repo even before the upstream landscape submission is opened.
13 changes: 13 additions & 0 deletions assets/tritrpc.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 7 additions & 3 deletions go/tritrpcv1/envelope.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ func lenPrefix(b []byte) []byte {
}

func BuildEnvelope(service, method string, payload []byte, aux []byte, aeadTag []byte, aeadOn bool, compress bool) []byte {
mode := TritPack243([]byte{0})
return BuildEnvelopeWithMode(service, method, payload, aux, aeadTag, aeadOn, compress, mode)
}

func BuildEnvelopeWithMode(service, method string, payload []byte, aux []byte, aeadTag []byte, aeadOn bool, compress bool, modeBytes []byte) []byte {
out := make([]byte, 0)
out = append(out, lenPrefix(MAGIC_B2)...)
out = append(out, MAGIC_B2...)
Expand All @@ -34,9 +39,8 @@ func BuildEnvelope(service, method string, payload []byte, aux []byte, aeadTag [
out = append(out, lenPrefix(ver)...)
out = append(out, ver...)

mode := TritPack243([]byte{0})
out = append(out, lenPrefix(mode)...)
out = append(out, mode...)
out = append(out, lenPrefix(modeBytes)...)
out = append(out, modeBytes...)

flags := TritPack243(flagsTrits(aeadOn, compress))
out = append(out, lenPrefix(flags)...)
Expand Down
8 changes: 8 additions & 0 deletions go/tritrpcv1/envelope_decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type Envelope struct {
Magic []byte
Version []byte
Mode []byte
ModeTrit byte
Flags []byte
Schema []byte
Context []byte
Expand Down Expand Up @@ -79,6 +80,12 @@ func DecodeEnvelope(frame []byte) (*Envelope, error) {
aeadOn := len(trits) > 0 && trits[0] == 2
compress := len(trits) > 1 && trits[1] == 2

modeTrits, _ := TritUnpack243(mode)
var modeTrit byte
if len(modeTrits) > 0 {
modeTrit = modeTrits[0]
}

var aux []byte
var tag []byte
tagStart := -1
Expand Down Expand Up @@ -118,6 +125,7 @@ func DecodeEnvelope(frame []byte) (*Envelope, error) {
Magic: append([]byte{}, magic...),
Version: append([]byte{}, version...),
Mode: append([]byte{}, mode...),
ModeTrit: modeTrit,
Flags: append([]byte{}, flags...),
Schema: append([]byte{}, schema...),
Context: append([]byte{}, context...),
Expand Down
64 changes: 19 additions & 45 deletions go/tritrpcv1/fixtures_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package tritrpcv1

import (
"bufio"
"crypto/subtle"
"encoding/hex"
"golang.org/x/crypto/chacha20poly1305"
"os"
"path/filepath"
"strings"
"testing"

"bufio"

"golang.org/x/crypto/blake2b"
)

func fixturePath(name string) string {
Expand Down Expand Up @@ -37,28 +39,6 @@ func readPairs(t *testing.T, path string) [][2][]byte {
return out
}

func readNonces(t *testing.T, path string) map[string][]byte {
t.Helper()
f, err := os.Open(path)
if err != nil {
t.Fatalf("open nonce file %s: %v", path, err)
}
defer f.Close()
sc := bufio.NewScanner(f)
out := map[string][]byte{}
for sc.Scan() {
ln := sc.Text()
if ln == "" {
continue
}
parts := strings.SplitN(ln, " ", 2)
key := parts[0]
b, _ := hex.DecodeString(parts[1])
out[key] = b
}
return out
}

func splitFields(buf []byte) [][]byte {
fields := [][]byte{}
off := 0
Expand All @@ -82,17 +62,16 @@ func aeadBit(flags []byte) bool {
}

func TestFixturesAEADAndPayloads(t *testing.T) {
sets := [][2]string{
{"vectors_hex.txt", "vectors_hex.txt.nonces"},
{"vectors_hex_stream_avrochunk.txt", "vectors_hex_stream_avrochunk.txt.nonces"},
{"vectors_hex_unary_rich.txt", "vectors_hex_unary_rich.txt.nonces"},
{"vectors_hex_stream_avronested.txt", "vectors_hex_stream_avronested.txt.nonces"},
{"vectors_hex_pathB.txt", "vectors_hex_pathB.txt.nonces"},
sets := []string{
"vectors_hex.txt",
"vectors_hex_stream_avrochunk.txt",
"vectors_hex_unary_rich.txt",
"vectors_hex_stream_avronested.txt",
"vectors_hex_pathB.txt",
}
key := [32]byte{}
for _, s := range sets {
pairs := readPairs(t, fixturePath(s[0]))
nonces := readNonces(t, fixturePath(s[1]))
for _, fx := range sets {
pairs := readPairs(t, fixturePath(fx))
for _, p := range pairs {
name := string(p[0])
frame := p[1]
Expand All @@ -110,7 +89,7 @@ func TestFixturesAEADAndPayloads(t *testing.T) {
if hex.EncodeToString(env.Context) != hex.EncodeToString(CONTEXT_ID_32) {
t.Fatalf("context id mismatch %s", name)
}
repacked := BuildEnvelope(env.Service, env.Method, env.Payload, env.Aux, env.Tag, env.AeadOn, env.Compress)
repacked := BuildEnvelopeWithMode(env.Service, env.Method, env.Payload, env.Aux, env.Tag, env.AeadOn, env.Compress, env.Mode)
if hex.EncodeToString(repacked) != hex.EncodeToString(frame) {
t.Fatalf("repack mismatch %s", name)
}
Expand All @@ -121,21 +100,16 @@ func TestFixturesAEADAndPayloads(t *testing.T) {
t.Fatalf("aad error %s: %v", name, err)
}
tag := env.Tag
n := nonces[name]
if len(n) != 24 {
t.Fatalf("nonce size mismatch %s", name)
}
if len(tag) != 16 {
t.Fatalf("tag size mismatch %s", name)
}
a, _ := chacha20poly1305.NewX(key[:])
strict := os.Getenv("STRICT_AEAD") == "1"
ct := a.Seal(nil, n, []byte{}, aad)
computed := ct[len(ct)-16:]
mac, err := blake2b.New(16, key[:])
if err != nil {
t.Fatalf("blake2b init: %v", err)
}
mac.Write(aad)
computed := mac.Sum(nil)
if subtle.ConstantTimeCompare(computed, tag) != 1 {
if strict {
t.Fatalf("strict AEAD tag mismatch for %s", name)
}
t.Fatalf("tag mismatch for %s", name)
}
}
Expand Down
2 changes: 2 additions & 0 deletions go/tritrpcv1/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ module github.com/example/tritrpcv1
go 1.21

require golang.org/x/crypto v0.24.0

require golang.org/x/sys v0.21.0 // indirect
18 changes: 15 additions & 3 deletions go/tritrpcv1/pathb_dec.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
package tritrpcv1

import "fmt"

// Minimal Path-B decoders for strings and union index (subset used in fixtures)
func PBDecodeLen(buf []byte, off int) (int, int) {
// TLEB3 decode for length: reuse TLEB3 decoder by repacking; here we assume small inputs and just reuse TritUnpack on a byte-by-byte basis
// NOTE: For production, implement a proper scanner.
trits := []byte{}
start := off
for {
if off >= len(buf) {
panic("EOF in PBDecodeLen")
}
b := buf[off]
off++
ts, _ := TritUnpack243([]byte{b})
var ts []byte
if b >= 243 && b <= 246 {
if off >= len(buf) {
panic(fmt.Sprintf("truncated tail marker in PBDecodeLen at offset %d", off))
}
ts, _ = TritUnpack243([]byte{b, buf[off]})
off++
} else {
ts, _ = TritUnpack243([]byte{b})
}
trits = append(trits, ts...)
if len(trits) >= 3 {
v := uint64(0)
Expand Down
27 changes: 20 additions & 7 deletions go/tritrpcv1/tleb3.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,28 @@ func TLEB3EncodeLen(n uint64) []byte {

func TLEB3DecodeLen(buf []byte, offset int) (val uint64, newOff int, err error) {
trits := []byte{}
off := offset
pos := offset
for {
if off >= len(buf) {
if pos >= len(buf) {
return 0, 0, errors.New("EOF in TLEB3")
}
b := buf[off]
off++
ts, _ := TritUnpack243([]byte{b})
b := buf[pos]
pos++
var ts []byte
// Tail-marker bytes (0xF3..=0xF6) span two bytes; pass both to TritUnpack243.
if b >= 0xF3 && b <= 0xF6 {
if pos >= len(buf) {
return 0, 0, errors.New("truncated TLEB3 tail marker")
}
b2 := buf[pos]
pos++
ts, err = TritUnpack243([]byte{b, b2})
} else {
ts, err = TritUnpack243([]byte{b})
}
if err != nil {
return 0, 0, err
}
trits = append(trits, ts...)
if len(trits) < 3 {
continue
Expand All @@ -56,8 +70,7 @@ func TLEB3DecodeLen(buf []byte, offset int) (val uint64, newOff int, err error)
}
if used > 0 {
pack := TritPack243(trits[:used])
usedBytes := len(pack)
return v, offset + usedBytes - 1 + (off - offset), nil
return v, offset + len(pack), nil
}
}
}
30 changes: 30 additions & 0 deletions go/tritrpcv1/tritrpcv1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,33 @@ func TestTleb3EncodeLen(t *testing.T) {
}
}
}

func TestTLEB3DecodeLenTailMarker(t *testing.T) {
// Verify that tail-marker lengths decode correctly and newOff accounts for all bytes
cases := []struct {
name string
buf []byte
offset int
wantV uint64
wantOff int
}{
{"len=0", []byte{0xF5, 0x00}, 0, 0, 2}, // [0,0,0] tail marker → 0
{"len=2", []byte{0xF5, 0x02}, 0, 2, 2}, // [0,0,2] tail marker → 2
{"len=32", []byte{0xD0, 0xF3, 0x00}, 0, 32, 3}, // 5+1 trits spanning regular+tail
{"len=9", []byte{0xA2, 0xF3, 0x01}, 0, 9, 3}, // 9 spans tail marker
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
v, no, err := TLEB3DecodeLen(c.buf, c.offset)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if v != c.wantV {
t.Errorf("value: got %d, want %d", v, c.wantV)
}
if no != c.wantOff {
t.Errorf("newOff: got %d, want %d", no, c.wantOff)
}
})
}
}
1 change: 1 addition & 0 deletions rust/tritrpc_v1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target/
30 changes: 30 additions & 0 deletions rust/tritrpc_v1/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion rust/tritrpc_v1/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ license = "MIT"
description = "TritRPC v1 reference (Rust): TritPack243, TLEB3, Envelope + AEAD (XChaCha20-Poly1305)."

[dependencies]
chacha20poly1305 = "0.10"
blake2 = "0.10"
chacha20poly1305 = { version = "0.10" }

serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Expand Down
Loading
Loading