# 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:

| Layer | Role | capOS relevance |
|-------|------|-----------------|
| OCapN Model | Abstract passable value model shared across languages. | Defines which values can cross a capability-network boundary. |
| Syrup | Canonical 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. |
| Locators | Peer and sturdyref identity syntax. | Prior art for durable object references and bootstrap URIs. |
| Netlayers | Transport abstraction for secure ordered channels. | Strong precedent for separating object protocol from TCP/TLS/Tor/libp2p/etc. |
| CapTP | Session protocol for messages, promises, GC, and handoffs. | Directly informs future network-transparent capability invocation. |
| Test suite | Interoperability 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:

```text
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?

## Recommended Near-Term Use

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
