Skip to content

NNRP/1 Cache Capabilities and Leases

Caching is not a private runtime optimization trick. It is a public capability boundary that the protocol needs to describe explicitly.

Where this fits in the protocol stack

During the handshake both sides negotiate a cache capability ceiling. When the session opens the host declares lease expectations. Long-lived objects are then installed via the control plane. Hot-path frames reference them by key instead of retransmitting the body.

Canonical cache identity

NNRP/1 uses one cache identity in every control-plane and hot-path representation:

text
(cache_namespace: u32, cache_key_hi: u64, cache_key_lo: u64, object_kind: u32 enum)

The two key words form one opaque 128-bit value. Implementations must not truncate either word or derive a second, transport-local key. A namespace scopes allocation and bulk invalidation; it remains part of the identity even when the key is globally unique. object_kind prevents equal keys for different object families from aliasing in local object and lease tables. The 24-byte hot-path object-reference block uses a validated u16 projection of this enum; that compact wire field does not narrow the canonical local identity or FFI representation.

CACHE_INVALIDATE applies its fields as follows:

ScopeRequired identity fieldsFields that must be zero
whole_sessionNonecache_namespace, cache_key_hi, cache_key_lo
namespacecache_namespacecache_key_hi, cache_key_lo
object_kindcache_namespace; cache_key_hi carries the u32 object-kind code in its low 32 bitsHigh 32 bits of cache_key_hi, all of cache_key_lo
object_keyFull cache identityNone

Local cache lease state

Every SDK uses the following validated value model for a granted cache lease. This model is local runtime state, not a second wire layout and not a native pointer or handle.

Semantic fieldWidthMeaning
object_idstructuredCanonical (cache_namespace, cache_key_hi, cache_key_lo, object_kind) identity.
object_versionu64Exact object version covered by the lease.
lease_idu64Runtime-issued lease identity.
owner_scopeu8 enumconnection = 0, session = 1, or operation = 2.
owner_idu64Identifier in the namespace selected by owner_scope.
granted_at_msu64Monotonic runtime timestamp at which the lease was granted.
ttl_msu32Granted lease lifetime in milliseconds.

SDKs may expose idiomatic field names, but must preserve the values and widths above. The expiration timestamp is saturating_add(granted_at_ms, ttl_ms). A lease is live only while now_ms < expires_at_ms, and using it for an object version other than object_version must fail with version_mismatch. A zero TTL is therefore immediately expired.

Cache wire metadata

All integer fields are little-endian. Reserved fields must be zero when sent and rejected when nonzero.

Object Reference Block

This 24-byte block appears in the hot-path object-reference region.

OffsetFieldTypeMeaning
0object_kindu16Cached object kind.
2ref_flagsu16Reference flags.
4cache_namespaceu32Cache namespace.
8cache_key_hiu64High 64 bits of cache key.
16cache_key_lou64Low 64 bits of cache key.

Cache Put Metadata

OffsetFieldTypeMeaning
0cache_namespaceu32Cache namespace.
4object_kindu32Cached object kind.
8cache_key_hiu64High 64 bits of cache key.
16cache_key_lou64Low 64 bits of cache key.
24ttl_msu32Requested lease TTL.
28object_bytesu32Object body length.
32codec_bitmapu32Permitted object codecs.
36flagsu32Put and renewal flags.

Cache Ack Metadata

OffsetFieldTypeMeaning
0cache_namespaceu32Cache namespace.
4statusu32Acceptance status.
8cache_key_hiu64High 64 bits of cache key.
16cache_key_lou64Low 64 bits of cache key.
24accepted_ttl_msu32Granted lease TTL.
28max_object_bytesu32Receiver object-size ceiling.
32detail_codeu32Status detail.
36reservedu32Must be zero.

Cache Invalidate Metadata

OffsetFieldTypeMeaning
0invalidate_scopeu32Invalidation scope.
4cache_namespaceu32Namespace selector.
8cache_key_hiu64High 64 bits of key or object-kind selector.
16cache_key_lou64Low 64 bits of key.
24reason_codeu32Invalidation reason.
28reservedu32Must be zero.

Object lifecycle sequence

This shows a long-lived object going from installation to active reference and then expiry:

Why leases are necessary

"Can be cached" is not enough. An object pool without expiry creates three problems:

  • The server cannot safely reclaim memory even when objects have not been used for a long time.
  • The host does not know which objects are still valid and must worry about cache miss on every submit.
  • When a model is updated or the context switches, stale objects have no clear decommission path.

Leases give every object a visible TTL and a renewal path so that both sides can act on protocol events rather than guessing from timeouts.

What the public layer freezes vs what belongs to profiles

Public layer (shared by all profiles)Profile / Runtime private
Lease contract (TTL, renewal, expiry policy)Object body byte layout
Object identity (kind, namespace, version)KV-cache page encoding
Dependency relation semanticsGPU memory page layout
cache_miss / lease_expired / dependency_invalid errorsModel-private index structures

Best practices

When to install: Only put objects in the cache if they will actually be referenced multiple times. Single-use small blocks should be inlined. As a rule of thumb, objects larger than 1 KB that will be reused more than twice in the same session are worth caching.

Choosing TTL: Set lease_ttl_hint_ms to 20–30 % shorter than the expected session duration. If the session is expected to last 60 seconds, set TTL to 40 seconds and renew proactively while the object is still in use rather than reinstalling it after expiry.

Handling invalidation: When you receive CACHE_INVALIDATE, immediately mark the local reference invalid and switch back to an inline block on the next submit. Never assume the same key is still live.

Version management: When object content changes, use a new cache_key instead of overwriting the old one. This prevents the server and host from disagreeing about which version of content a key currently refers to.

Observability: Record actual_ttl_ms from every CACHE_ACK, the reason field from every CACHE_INVALIDATE, and the hit/miss ratio. These are the only stable signals for evaluating whether your caching strategy is working.

Boundaries with other pages

  1. Responsibility boundaries for connection, session, and operation — see "Session and Operation Model".
  2. Fixed layout of descriptors and payloads — see "Typed Payload Descriptors" and the profile pages.
  3. How schema becomes the standard extension mechanism — see the next page, "Schema / Profile Registry".

NNRP Documentation