Presence and payload references
The remaining App operations expose current connection metadata and a bounded way to give a client access to a value stored in history. They use the same identity scope as command and history operations.
Query current presence
clients = await scoped.get_presence()
The result contains AgentPresence records for active registry entries within the bound user:
| Field | Meaning |
|---|---|
client_id | stable logical client installation |
instance_id | current running lifetime registered for that client |
connected_at | registration time for the active runtime |
last_seen | most recent registry liveness evidence |
client_type | advertised environment type when supplied |
actions | action names advertised by this runtime |
client_metadata | application-defined client metadata |
metadata | complete retained connection metadata |
An optional selector performs exact key-value matching against the connection metadata:
browsers = await scoped.get_presence(
selector={"client_type": "browser"},
)
Presence is a registry observation, not a permanent guarantee. A connection can disappear after the query, and a stale record can remain until heartbeat, TTL, or sweep behavior removes it. Command admission performs its own freshness and bounded-reconnect checks. Retained client-sighting evidence is deliberately not returned as active presence and cannot be used as routing authority.
The public App method does not expose the registry's specialized get_by_action() or get_by_metadata() operations. Product code can normally filter the returned scoped presence or use the selector without taking a dependency on a concrete registry implementation.
Store an application value in history
stream_key, message_id = await scoped.store_payload(
client_id="primary-browser",
stream_suffix="reports",
payload={"report": report_data},
)
store_payload() writes an offloaded_payload history message in the bound user's canonical stream. It returns the full stream key and message ID. Supplying an explicit message ID uses the history store's stream-scoped idempotency behavior; otherwise App generates one.
This operation stores data but does not create an access token. It can be used when server code needs an ordinary history-backed value or when token creation is handled separately.
Store and create a bounded read token
token = await scoped.store_and_sign(
client_id="primary-browser",
stream_suffix="reports",
payload={"report": report_data},
expires_in_ms=300_000,
)
store_and_sign() combines storage with a signed reference. The default token lifetime is five minutes. The claims bind the full stream key, message ID, effective user, and read scope. A configured token signer is required; otherwise the method raises RuntimeError.
Raw AppNode.sign_ref() signs a reference to an already stored message without performing a history write:
token = await app_node.sign_ref(
routing,
stream_key,
message_id,
expires_in_ms=300_000,
)
It rejects a stream key outside the routing user's prefix. It does not read history to prove that the message exists, so callers must pass a reference obtained from trusted storage logic. The scoped App wrapper exposes store_payload() and store_and_sign() but not a direct sign_ref() shortcut.
Retrieve through the authenticated REST edge
The host exposes a route that establishes the trusted user or tenant principal and calls RestNode.retrieve_payload(token, identity). Retrieval verifies the signature and expiry, requires scope="read", compares the token user with the authenticated edge identity, checks the stream prefix, and looks up the message in history.
The service result distinguishes:
200with the stored payload;403for invalid, expired, wrong-user, wrong-scope, or malformed claims;404when the referenced message is not retained;500when payload retrieval has no token signer configured.
The token is a bounded data reference, not a replacement for HTTP authentication. The host still supplies the independently verified identity used for the comparison.
Explicit references and automatic command offloading
These helpers are also used by App's automatic command-size handling. When AppNodeConfig.max_ws_message_bytes is set, a command exceeds that size, auto_offload=True, and a signer is available, App can move supported large command fields into history and send signed references instead. Unsupported shapes or a command that remains too large fail with DispatchError.
Explicit store_and_sign() remains useful for application-owned values that are not created by command packaging. In both cases, retention and token lifetime need to be coordinated: a valid token cannot retrieve a history message that has already expired or been deleted.
Return to Application API or continue to the exact returned-message and state protocol.