Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Research: Spritely, OCapN, and CapTP

Research note last checked 2026-04-30. This file records the related specifications, protocols, and design principles behind Spritely’s OCapN/CapTP work and translates them into capOS design consequences. It intentionally summarizes the specifications rather than copying them; the upstream documents are draft standards and should remain the source of truth.

Executive Summary

Spritely’s most relevant contribution for capOS is not a single library. It is a coherent model for secure distributed object programming:

  • Authority is an unforgeable object reference. If a peer was not handed a reference, it cannot use the object.
  • Object references can cross a network without turning into global names or ACL checks. References remain local session table entries, generally cheap integer positions between the two peers that share a CapTP session.
  • Networking is explicit at the implementation boundary but mostly absent from application object design. A program can pass references and send messages to asynchronous objects without inventing a bespoke protocol for every service.
  • Latency is handled by promise pipelining. Dependent messages can be sent to the eventual result of an earlier message before the earlier message settles.
  • Resource lifetime is part of the protocol. CapTP includes cooperative distributed garbage collection for exported references and answer promises.
  • Third-party handoffs solve the hard case where A gives B a reference to an object hosted by C without making A a permanent proxy.

The design is close enough to capOS’s schema-as-ABI direction to matter: capOS already treats typed Cap’n Proto interfaces as authority boundaries and has reserved ring fields for future promise pipelining. OCapN/CapTP gives a prior-art shape for the next network-transparent capability layer, but the current OCapN documents are still drafts and should not be adopted as frozen wire compatibility commitments.

Source Map

Primary sources read:

  • Spritely Institute:
    • “What is CapTP, and what does it enable?”
    • “Introducing OCapN, interoperable capabilities over the network”
    • “The Heart of Spritely: Distributed Objects and Capability Security”
    • Spritely Goblins 0.18.0 release notes
    • Guile Goblins manual sections for OCapN and CapTP
  • OCapN draft specifications:
    • CapTP Specification.md
    • Model.md
    • Netlayers.md
    • Locators.md
    • Syrup repository and draft specification material
  • Related lineage and implementations:
    • Cap’n Proto RPC protocol documentation
    • Endo @endo/ocapn documentation
    • E / CapTP lineage as summarized by Spritely, OCapN, and Cap’n Proto docs

The OCapN draft repository HEAD observed during this pass was 18400d8508fb67467da6d659412ae19c27b0cd08. The Syrup repository HEAD observed was 931fa528b8ddda976febba577fb09ee0726845d4.

Current Status

The old spritelyproject.org site is historical. Active project material is now under the Spritely Institute site and files.spritely.institute.

OCapN is not yet a final standard. The draft specs explicitly warn that they are likely to change significantly. Spritely’s 2026-04-21 Goblins 0.18.0 release is a useful data point: it changed OCapN protocol details, removed the old op:deliver-only operation, renamed GC operations to plural batched forms, and bumped the protocol version incompatibly with earlier Goblins releases.

For capOS this means:

  • Use OCapN/CapTP as design grounding, not as a frozen ABI.
  • Avoid promising wire-level OCapN compatibility until a concrete version is selected and a test-suite target exists.
  • Keep capOS’s own ring and schema ABI evolution policy independent from OCapN draft churn.

Spritely System Model

Spritely Goblins is a distributed object programming environment. Its core objects are actors. Actors live in vats/actormaps, receive messages, and may evolve by returning replacement behavior rather than mutating global ambient state. The programming model is explicitly object-capability based: references are authority, and authority flows by ordinary reference passing.

Spritely adds several properties that are relevant to OS design:

  • Transactional turns. Local synchronous object updates happen in turns that can roll back on failure. This keeps partial state updates from becoming visible after an exception.
  • Asynchronous references and promises. The same programming model handles local and remote asynchronous objects.
  • Persistence and sleeping actors. Goblins can persist actor state and, in 0.18.0, optionally evict actors from the hot cache while retaining live references that wake them on demand.
  • Distributed debugging and time travel. Spritely treats deterministic turns and persistent state as debugging tools, not only durability features.

capOS should not copy Goblins’ language runtime shape into the kernel. The usable lesson is the boundary: keep kernel capability objects small and typed, while allowing userspace runtimes to build richer object, promise, rollback, and persistence semantics above them.

Object-Capability Principles

The Spritely/OCapN material uses classic object-capability principles:

  • No ambient authority. Code begins without dangerous authority and gains power only through values it is passed.
  • Designation is authorization. The reference both names the object and grants the right to invoke it.
  • Attenuation by wrapping. A narrower object can hold a broader object and expose only a smaller method surface or policy-filtered behavior.
  • Revocation by indirection. A revoker can sit between holder and target and later stop forwarding.
  • Accountability by explicit relationship. Authority flow is visible as graph edges between objects, not hidden inside a global namespace.
  • Mutual suspicion. A remote peer is not trusted just because the transport is authenticated; it is treated as a potentially adversarial object holding only the capabilities it has received.

This matches capOS’s existing direction: typed interfaces define permission surfaces, and narrower capabilities are preferable to broad rights bitmasks attached to generic handles.

OCapN Protocol Suite

OCapN is a suite, not just CapTP. The important layers are:

LayerRolecapOS relevance
OCapN ModelAbstract passable value model shared across languages.Defines which values can cross a capability-network boundary.
SyrupCanonical binary serialization used by current OCapN drafts.Useful for signed certificates and dynamic interop, but not a replacement for capOS’s Cap’n Proto schema ABI.
LocatorsPeer and sturdyref identity syntax.Prior art for durable object references and bootstrap URIs.
NetlayersTransport abstraction for secure ordered channels.Strong precedent for separating object protocol from TCP/TLS/Tor/libp2p/etc.
CapTPSession protocol for messages, promises, GC, and handoffs.Directly informs future network-transparent capability invocation.
Test suiteInteroperability tests for implementations.capOS should not claim OCapN compatibility without passing a selected suite/version.

OCapN Data Model

The model draft defines passable values as atoms, containers, references, and errors.

Atoms:

  • Undefined
  • Null
  • Boolean
  • arbitrary precision signed Integer
  • IEEE 754 Float64
  • Unicode String
  • ByteArray
  • Symbol

Containers:

  • List
  • unordered string-keyed Struct
  • Tagged, a tag string plus one value

References:

  • Target, an object reference that can receive messages
  • Promise, a pending eventual value that can queue messages

Errors are still unsettled in the model draft. The draft preserves only the coarse requirement that an error round trip as an error. This is weaker than capOS’s desired error-layer split, so capOS should keep its local rule: transport status in CQEs, capability infrastructure failure in CapException, and domain outcomes in schema result unions.

The model also defines pass invariants. The important one for capOS is that remote passage should preserve type, and for most values preserve a specified equality relation when values leave and later return. Promises and errors are special: promises preserve type but not identity equality, and error semantics are deliberately not settled yet.

Syrup

Syrup is a canonical binary serialization format used by OCapN drafts. It is inspired by canonical s-expressions and bencode. It supports booleans, integers, floats, byte strings, strings, symbols, lists, dictionaries/structs, records, and sets. Its important property for CapTP is canonicalization: unordered collections are emitted in a deterministic order, so serialized bytes can be signed and verified consistently.

This matters most for OCapN handoff certificates. A signed envelope signs the canonical serialized form of a CapTP object, so implementations need byte-stable encoding.

capOS implications:

  • Keep using Cap’n Proto for typed capOS ABIs and kernel/userspace messages.
  • Treat Syrup as an interop codec for a future OCapN bridge, not as the native kernel ring format.
  • If capOS implements OCapN handoffs, canonical serialization becomes part of the trusted boundary. Fuzzing and cross-implementation test vectors would be mandatory.

Locators and Sturdyrefs

OCapN locators represent peers and durable object entry points.

A peer locator contains:

  • transport: the netlayer name
  • designator: usually a key or other netlayer-defined identity
  • hints: optional routing data

Only transport and designator identify the peer for comparison. Hints can help connection setup but do not define identity.

A sturdyref locator contains:

  • a peer locator
  • a swiss-num, a secret-ish object token used to fetch a specific object from that peer’s bootstrap object

URI forms include:

ocapn://<designator>.<transport>
ocapn://<designator>.<transport>/s/<swiss-num>

The draft states that a sturdyref should be treated as a capability: the locator plus swiss number is enough to try to obtain the object reference.

capOS implications:

  • A future durable capOS network reference must not be confused with a local CapId. Local cap slots, generations, receiver selectors, session ids, and kernel object pointers are not portable authority.
  • If capOS adds sturdyrefs, they belong in a userspace naming/storage authority or broker, not in the kernel cap table.
  • Hints must never become security identity. They are routing metadata only.
  • Swiss-number strength and storage policy are security-critical; weak or enumerable swiss numbers would become bearer-token vulnerabilities.

Netlayers

OCapN netlayers are the transport interface underneath CapTP. A compliant netlayer provides:

  • bidirectional message transmission
  • delivery while the session remains active
  • in-order receipt
  • security against third-party message insertion

Encryption and reachability are desirable and often necessary, but the netlayer draft distinguishes required session integrity from optional transport properties. The Tor Onion netlayer is documented in the draft; Spritely Goblins has historically emphasized Tor, while OCapN discussions also mention TCP/TLS, WebSocket, libp2p, IBC, I2P, Unix sockets, and other transports.

capOS implications:

  • Follow the OCapN split: object protocol above transport authority.
  • Represent listen/connect authority as explicit capabilities, as capOS already does for narrowed TCP listener authority.
  • Bind peer identity to the netlayer’s authenticated designator, not to DNS names, host strings, or untrusted hints.
  • Treat reconnect and disconnect as first-class protocol states. All remote capabilities served by a severed session must fail closed or become broken promises.

CapTP Session Establishment

A CapTP session is pairwise. It runs over a reliable ordered netlayer channel. The draft session setup:

  • establish a secure channel out of band or as part of a handoff
  • create a per-session cryptographic key pair
  • exchange op:start-session
  • verify the remote session start message
  • export a bootstrap object at position 0

The bootstrap object conventionally supports:

  • fetch, to fetch an object by swiss number
  • deposit-gift, for third-party handoffs
  • withdraw-gift, for third-party handoffs

capOS implications:

  • A remote session needs an explicit session object with state, cryptographic identity, import/export tables, answer table, handoff table, and disconnect state.
  • Bootstrap authority should be narrow. A peer’s bootstrap object is the initial remote authority root and should expose only intended fetch/handoff behavior.
  • A future capOS OCapN bridge should make protocol version negotiation and feature gating explicit because upstream OCapN has already changed incompatibly.

CapTP References and Descriptors

CapTP references are represented by descriptors whose integer positions have meaning only within a single session. The key descriptor families are:

  • desc:import-object: the receiver is importing an object at a position
  • desc:import-promise: the receiver is importing a promise at a position
  • desc:export: refer to an object/promise already exported by the receiving side
  • desc:answer: refer to a promise created by a previous answer position
  • desc:sig-envelope: signed wrapper over a canonical serialized CapTP object
  • desc:handoff-give: gift certificate from gifter to receiver
  • desc:handoff-receive: receiver certificate used to redeem a gift

The subtle convention is perspective: descriptors describe references from the receiver’s side of the session. This keeps pairwise table entries small but requires careful implementation.

capOS implications:

  • Do not serialize process-local cap ids across a network.
  • Network references need a separate table keyed by session-local import/export position and generation or epoch.
  • Descriptor direction needs tests. Perspective errors here become authority leaks or denial of service bugs.

CapTP Operations

The current CapTP draft includes operations in these groups:

  • session lifecycle: op:start-session, op:abort
  • delivery: op:deliver
  • promise observation/resolution: op:listen, promise resolver fulfill and break behavior
  • promise pipelining and extraction: op:get, op:index, op:untag
  • cooperative GC: op:gc-exports, op:gc-answers
  • handoff bootstrapping through bootstrap methods: deposit-gift, withdraw-gift

op:deliver-only should be treated as stale for current research because Goblins 0.18.0 and the current draft dropped it in favor of op:deliver.

capOS implications:

  • capOS’s reserved pipeline_dep / answer-id style fields should be evaluated against CapTP’s answer table model.
  • op:get, op:index, and op:untag show that pipelining is not only “call a method on a promised object”; it can also project a reference out of an eventual container without transmitting irrelevant intermediate values.
  • Batched GC operations are an important shape for avoiding per-reference chatter.

Promise Pipelining

Promise pipelining is the latency-critical idea shared by E, Cap’n Proto RPC, Agoric/Endo, and OCapN. If a call returns a promise for an object, the caller can immediately send follow-on messages to the promised result. The receiver queues or forwards those messages when the promise resolves.

This preserves object-shaped interfaces in high-latency networks. Without pipelining, developers tend to collapse clean object graphs into singleton services with path strings or ad hoc batching APIs, weakening both design and authority boundaries.

capOS implications:

  • Promise pipelining is a Tier-1 paper evidence candidate in docs/roadmap.md; this research reinforces that priority.
  • Pipelining should target result-cap/answer namespaces, not caller-selected global ids.
  • Broken promises must propagate failure to dependent calls. Silent drops would violate caller expectations and leak resources.
  • Pipelined calls must remain bounded by resource ledgers: answer table slots, queued message bytes, queued call count, and per-session memory all need caps.

Distributed Garbage Collection

CapTP uses cooperative distributed GC for references exported across a session. At a high level:

  • When a reference is exported, the exporting side keeps it alive on behalf of the importing side.
  • The importer tracks how many times it received the reference.
  • When the importer no longer needs the reference, it sends batched GC deltas.
  • The exporter decrements its per-session reference count and may reclaim once the count reaches zero.
  • Answer promises also have explicit op:gc-answers cleanup so answer positions can be reused.

The Goblins docs call this acyclic distributed GC. Cycles spanning machines are not automatically collected in the deployed Guile Goblins path.

capOS implications:

  • Network reference release must be explicit and idempotent under disconnect and retry conditions.
  • Reference accounting must have one ledger of record per session. Parallel counters in transport, object proxy, and app layers would be unreviewable.
  • A capOS bridge should not rely on distributed cycle collection. Design protocols so remote cycles are either impossible, bounded by lease/session lifetime, or broken by explicit revocation.
  • Disconnect should conservatively release exports owned solely by the session and break unresolved imports/promises.

Third-Party Handoffs

Third-party handoffs solve the case where A has a reference to an object hosted by C and sends that reference to B. A should not need to proxy every future call from B to C, and B should not gain arbitrary authority at C. The OCapN draft uses certificate-style gifts.

Roles:

  • Gifter: the peer sharing a reference it holds
  • Receiver: the peer receiving that reference
  • Exporter: the peer hosting/exporting the referenced object

Protocol shape:

  • The gifter deposits a gift with the exporter’s bootstrap object.
  • The gifter sends the receiver a signed desc:handoff-give.
  • The receiver validates what it can, connects to the exporter if needed, and sends a signed desc:handoff-receive to withdraw the gift.
  • The exporter verifies signatures, session ids, receiver binding, and replay protection, then fulfills the receiver’s promise with the gifted reference.

Security properties to preserve:

  • The gift is designated to a specific receiver session identity.
  • The exporter must reject invalid signatures or replayed handoff counts.
  • The handoff can complete whether deposit or withdrawal arrives first.
  • Unauthorized peers that observe messages should not be able to redeem the object reference.

capOS implications:

  • Handoffs are the correct precedent for cross-session capability transfer. Avoid proxy-only designs as the permanent architecture.
  • A capOS implementation needs persistent in-flight handoff state with bounded memory and expiry.
  • The replay counter/nonce table is security-sensitive. It should be scoped by exporter-receiver session and garbage collected with the session.
  • Handoff certificates should be opaque to ordinary applications unless a debugging authority is explicitly granted.

Error Propagation

OCapN and CapTP allow promises to break with an error value, but the data model has not converged on a rich normative error structure. The CapTP draft warns that transmitting exception details or backtraces can leak sensitive data.

capOS implications:

  • Keep the capOS error-layer split. OCapN errors should map into CapException or schema-level results only through a deliberate adapter.
  • Strip or seal debug details at network boundaries by default.
  • Treat remote error text as untrusted input. It is diagnostic material, not an authority decision input.

Security Risks and Failure Modes

Important risks found in the source material:

  • Spec churn. OCapN is draft/pre-standardization and has changed incompatibly.
  • Resource exhaustion. Goblins docs state that CapTP does not solve memory usage or resource management by itself.
  • Acyclic-only GC. Cycles between servers are not automatically reclaimed in current Goblins’ practical model.
  • Peer-wide trust boundary. Even if CapTP routes to specific objects, a malicious remote peer can collude internally. Treat the peer as a single adversarial object with the authority surface of all references it holds.
  • Signing-oracle bugs. Goblins 0.18.0 fixed a signing oracle vulnerability in a WebSocket netlayer designator-authentication path. This is a concrete reminder that handoff/netlayer signing APIs need strict domain separation.
  • Debug info leakage. Broken promises or exceptions can accidentally expose paths, stack traces, or internal object topology.
  • Replay and stale-reference bugs. Handoff counts, session ids, export positions, and answer positions require generation/reuse discipline.

capOS mitigations:

  • Version every network protocol boundary.
  • Bound every per-session table and queue with resource ledgers.
  • Domain-separate all signatures by protocol label, session id, role, and operation kind.
  • Fuzz canonical codec parsing and descriptor validation.
  • Add negative tests for stale answer positions, stale export positions, replayed handoffs, mismatched receiver keys, malformed locators, and disconnect during handoff.

Relationship to Cap’n Proto

Cap’n Proto RPC is a close relative rather than the same protocol:

  • It is schema-first and statically typed.
  • Interface references are first-class capabilities.
  • Promise pipelining is central.
  • Persistent capabilities and three-way interactions are defined as higher protocol levels.
  • Cap’n Proto RPC deliberately does not make remote calls look like local blocking calls; the API exposes promises and network failure.

capOS already uses Cap’n Proto for schemas and serialization, but not full capnp-rpc. OCapN’s dynamic model is useful for language-agnostic distributed objects; Cap’n Proto remains the better fit for capOS’s typed ABI and generated interface surface.

The practical direction for capOS:

  • Keep local kernel/userspace ABI fixed-layout where needed and Cap’n Proto schema-shaped at service boundaries.
  • Learn from OCapN’s session, handoff, locator, and GC machinery.
  • Do not replace typed schemas with untyped dynamic symbols unless building an explicit OCapN bridge.

Relationship to Agoric and Endo

Agoric and Endo continue the E-language object-capability lineage in hardened JavaScript. Endo’s @endo/ocapn docs describe a tentative OCapN implementation with layers for client/session management, CapTP dispatch and slot management, codecs, and netlayers. The package is explicitly a work in progress and treats OCapN as a moving target.

This independently validates the same architectural split:

  • object/capability semantics
  • session/slot management
  • canonical codec
  • netlayer abstraction
  • higher-level client API for sturdyrefs and handoffs

For capOS, that split is more important than JavaScript-specific APIs.

CapOS Design Consequences

  1. Keep CapId local. Never serialize local cap table ids, endpoint generations, receiver selectors, or kernel session ids as portable network authority.
  2. Treat remote references as session-local imports/exports with explicit generation/reuse rules.
  3. Put sturdyrefs and durable fetch authority in userspace naming/storage services, not in the kernel cap table.
  4. Keep network transport authority separate from object authority. A process may hold permission to listen/connect without holding permission to fetch a particular remote object, and vice versa.
  5. Implement promise pipelining through answer/result-cap namespaces. Avoid path-string singleton APIs created only to hide latency.
  6. Bound all per-session state: exports, imports, answers, queued pipelined deliveries, handoff gifts, handoff replay counters, incoming message bytes, and pending reconnects.
  7. Make disconnect semantics explicit. Remote refs become disconnected/broken, not silently retrying with ambient authority.
  8. Strip or seal diagnostic errors crossing a remote boundary.
  9. Use canonical serialization only where signatures require it. Do not move the kernel ring to Syrup.
  10. Defer OCapN compatibility claims until capOS targets a specific draft, version negotiation, and test suite.

Open Questions for capOS

  • Should capOS expose an OCapN bridge as a userspace service that maps OCapN targets to local typed Cap’n Proto capabilities, or should it first implement a Cap’n Proto RPC bridge for typed external clients?
  • What is the narrowest promise-pipelining proof that advances the paper track: local ring answer pipelining, capnp-rpc-compatible pipelining, or OCapN-like answer descriptors?
  • How should capOS represent durable remote authority: opaque broker-held sturdyrefs, sealed persistent capabilities, or storage-service entries that mint live session refs on demand?
  • Which cryptographic identity should a capOS netlayer use first: TLS certificates, Noise static keys, Tor Onion service ids, or a local test-only key?
  • How much of OCapN’s dynamic value model should be admitted at capOS service boundaries, given the existing schema-first security posture?

For current capOS work, this research should be used as grounding for:

  • promise pipelining design
  • network-transparent capability proxy experiments
  • Cap’n Proto RPC interop work
  • durable naming/sturdyref design
  • remote capability release and disconnect semantics
  • third-party capability handoff designs

It should not yet be used to require OCapN wire compatibility for existing capOS demos or to replace the typed Cap’n Proto service model.

Sources

  • Spritely Institute: https://spritely.institute/
  • What is CapTP, and what does it enable?: https://spritely.institute/news/what-is-captp.html
  • Introducing OCapN, interoperable capabilities over the network: https://spritely.institute/news/introducing-ocapn-interoperable-capabilities-over-the-network.html
  • Spritely Goblins v0.18.0 release notes: https://spritely.institute/news/spritely-goblins-v0-18-0-sleepy-actors.html
  • The Heart of Spritely: Distributed Objects and Capability Security: https://files.spritely.institute/papers/spritely-core.html
  • Guile Goblins CapTP manual: https://files.spritely.institute/docs/guile-goblins/0.17.0/CapTP-The-Capability-Transport-Protocol.html
  • Guile Goblins OCapN manual: https://files.spritely.institute/docs/guile-goblins/0.16.1/OCapN.html
  • OCapN draft specifications: https://github.com/ocapn/ocapn/tree/main/draft-specifications
  • CapTP draft specification: https://github.com/ocapn/ocapn/blob/main/draft-specifications/CapTP%20Specification.md
  • OCapN model draft: https://github.com/ocapn/ocapn/blob/main/draft-specifications/Model.md
  • OCapN netlayers draft: https://github.com/ocapn/ocapn/blob/main/draft-specifications/Netlayers.md
  • OCapN locators draft: https://github.com/ocapn/ocapn/blob/main/draft-specifications/Locators.md
  • Syrup repository: https://github.com/ocapn/syrup
  • Cap’n Proto RPC protocol: https://capnproto.org/rpc.html
  • Endo @endo/ocapn: https://docs.endojs.org/modules/_endo_ocapn.html