Agent Skills: Syrup: Binary Serialization for OCapN

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

UncategorizedID: plurigrid/asi/syrup

Install this agent skill to your local

pnpm dlx add-skill https://github.com/plurigrid/asi/tree/HEAD/skills/syrup

Skill Files

Browse the full folder contents for syrup.

Download Skill

Loading file tree…

skills/syrup/SKILL.md

Skill Metadata

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

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

| Type | Format | Example | |------|--------|---------| | 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 Case | Why Syrup | |----------|-----------| | CapTP messages | Wire format for capability transport | | Content addressing | Canonical = deterministic hashes | | Signing | Sign serialized form, verify anywhere | | Storage | Compact binary, self-describing | | Cross-language | Same bytes from Python/Guile/Racket |


Related Skills

| Skill | Relation | |-------|----------| | captp | CapTP uses Syrup as wire format | | preserves | Syrup is a Preserves serialization | | localsend-mcp | P2P transfer could use Syrup | | acsets | Serialize 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