Asi syrup

Syrup binary serialization for OCapN/CapTP wire format. Canonical encoding for capability messages.

install
source · Clone the upstream repo
git clone https://github.com/plurigrid/asi
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/plurigrid/asi "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/syrup" ~/.claude/skills/plurigrid-asi-syrup && rm -rf "$T"
manifest: skills/syrup/SKILL.md
source content

Syrup: Binary Serialization for OCapN

Trit: -1 (MINUS - constraining/validating serialization) Color: #8B4513 (Saddlebrown - like syrup) Source: ocapn/syrup


Overview

Syrup is a lightweight binary serialization format for the OCapN (Object Capability Network). It provides canonical encoding - the same data always serializes to identical bytes - making it suitable for hashing, signing, and capability transport.

Core principle: Simple, canonical, easy to implement across languages.


Type Encoding Reference

TypeFormatExample
Boolean
t
or
f
t
= true
Positive int
<digits>+
42+
Negative int
<digits>-
123-
= -123
Bytestring
<len>:<bytes>
3:cat
String
<len>"<utf8>
5"hello
Symbol
<len>'<utf8>
3'foo
Float
F<4 bytes>
IEEE 754 single
Double
D<8 bytes>
IEEE 754 double
List
[<items>]
[3+4+5+]
= [3,4,5]
Dict
{<k><v>...}
{3"age12+}
Set
#<items>$
#3:a3:b$
Record
<<label><args>>
<4"date2024+5+1+>

Installed Implementations

Python

# pip install via: gh repo clone ocapn/syrup && cd syrup/impls/python
from syrup import syrup_encode, syrup_decode, Symbol, record

# Encode
syrup_encode({"name": "alice", "age": 30})
# => b'{3"age30+4"name5"alice}'

# Decode  
syrup_decode(b'{3"age30+4"name5"alice}')
# => {'age': 30, 'name': 'alice'}

# Symbols (for Lisp-like keys)
syrup_encode({Symbol('species'): b'cat'})
# => b"{7'species3:cat}"

# Records (custom types)
syrup_encode(record('date', 2024, 5, 1))
# => b'<4"date2024+5+1+>'

Guile Scheme

;; Install: copy syrup.scm to your Guile load path
(use-modules (syrup))

;; Encode
(syrup-encode 42)
;; => #vu8(52 50 43)  ; "42+"

(syrup-encode '("hello" "world"))
;; => #vu8(91 53 34 104 101 108 108 111 53 34 119 111 114 108 100 93)

;; Decode
(syrup-decode #vu8(52 50 43))
;; => 42

;; Records
(syrup-encode (make-syrec* (string->utf8 "date") 2024 5 1))

Racket

#lang racket
(require syrup)

;; Encode
(syrup-encode #hash(("name" . "alice") ("age" . 30)))
;; => #"{3\"age30+4\"name5\"alice}"

;; Decode
(syrup-decode #"{3\"age30+}")
;; => #hash(("age" . 30))

;; Records
(syrup-encode (record* #"date" 2024 5 1))
;; => #"<4:date2024+5+1+>"

;; Marshalling custom types
(struct point (x y) #:transparent)
(syrup-encode (point 1 2)
              #:marshallers (list (cons point? 
                                       (λ (p) (record* 'point (point-x p) (point-y p))))))

Package:

syrup
(installed via
raco pkg
)


Canonical Property

Syrup guarantees canonical encoding:

  1. Dictionaries: Keys sorted by their serialized byte representation
  2. Sets: Items sorted by their serialized byte representation
  3. No duplicate keys: Dicts must not contain the same key twice

This means:

hash(syrup_encode(x)) == hash(syrup_encode(y))
iff
x == y


Backwards Compatibility

Syrup decoders accept:

  • Bencode:
    d3:agei12ee
    → dict
  • Canonical S-expressions:
    (3:foo 3:bar)
    → list
# Bencode
syrup_decode(b'd3:agei12e4:name5:Missye')
# => {b'age': 12, b'name': b'Missy'}

# S-expressions
syrup_decode(b'(3:cat 7:tabatha)')
# => [b'cat', b'tabatha']

GF(3) Triads

# Wire Format Bundle
syrup (-1) ⊗ captp (0) ⊗ gay-mcp (+1) = 0 ✓  [Colored Capabilities]
syrup (-1) ⊗ localsend-mcp (0) ⊗ tailscale-file (+1) = 0 ✓  [P2P Transfer]
syrup (-1) ⊗ beeper-mcp (0) ⊗ agent-o-rama (+1) = 0 ✓  [Message Encoding]

# Serialization Stack
syrup (-1) ⊗ preserves (0) ⊗ json (+1) = 0 ✓  [Format Spectrum]

Commands

# Python (after installing to PYTHONPATH)
python3 -c "from syrup import *; print(syrup_encode({'test': 42}))"

# Guile (after installing to load path)
guile -c "(use-modules (syrup)) (display (syrup-encode 42))"

# Racket (after raco pkg install)
racket -e "(require syrup) (displayln (syrup-encode #hash((\"x\" . 1))))"

Use Cases

Use CaseWhy Syrup
CapTP messagesWire format for capability transport
Content addressingCanonical = deterministic hashes
SigningSign serialized form, verify anywhere
StorageCompact binary, self-describing
Cross-languageSame bytes from Python/Guile/Racket

Related Skills

SkillRelation
captpCapTP uses Syrup as wire format
preservesSyrup is a Preserves serialization
localsend-mcpP2P transfer could use Syrup
acsetsSerialize ACSets with Syrup

Installation

# Clone the repo
gh repo clone ocapn/syrup

# Python: add to PYTHONPATH or copy syrup.py
cp syrup/impls/python/syrup.py ~/.local/lib/python3/

# Guile: copy to load path
cp syrup/impls/guile/syrup.scm ~/.local/share/guile/site/3.0/

# Racket: install package
cd syrup/impls/racket && raco pkg install syrup/

References


Skill Name: syrup
Type: Binary Serialization Format
Trit: -1 (MINUS)
GF(3): Constrains data to canonical form
Invariant: Same data → same bytes, always