Session Keys
A session key is an ephemeral Ed25519 keypair, identified by a did:key:z6Mk… DID, that the SDK generates client-side and the owner DID delegates a scoped capability set to at sign-in. The app — or agent — holds the session key and signs every UCAN sub-delegation and invocation with it, so the owner's wallet key is signed with exactly once (the SIWE root grant) and never held by the application.
Role
Session keys are Layer 1 identity — the working half of the two-DID model. They are why TinyCloud's authority model is safe to embed in untrusted clients: the only thing an app possesses is a short-lived key whose entire reach is bounded by the capability set the owner granted it (see attenuation, trust-model). Compromising a session key exposes only its scoped grant for its remaining lifetime, never the root.
Shape
- Key material: an Ed25519 JWK (
crv: "Ed25519"), generated byJWK::generate_ed25519()in the WASMSessionManager(packages/sdk-rs/src/session/manager.rs). Each key carries a stringkey_id(the JWKkid); the default id is"default", renamed at sign-in tosession-{timestamp}. - DID:
DIDKey::generate(jwk)→did:key:z6Mk…; the DID URL appends the key as a fragment:did:key:z6Mk…#z6Mk…(manager.rs:get_did). This DID is the audience of the owner's root delegation and the issuer of everything the session signs. - Lifetime: bounded by the session's SIWE/ReCap
expirationTime(the SDK clamps sub-delegations to the session expiry; an expired session throwsSessionExpiredError).
Mechanics
The WASM session manager
SessionManager (packages/sdk-rs/src/session/manager.rs, exposed to JS as TCWSessionManager) holds an in-memory HashMap<key_id, SessionInfo> of session keypairs plus the in-progress ReCap Capability. Its surface:
new()— generates the initial"default"Ed25519 key.create_session_key(id)/import_session_key(jwk, id, override)— add keys; duplicate ids error unless overridden.rename_session_key_id(old, new)— the SDK renames"default"→session-{ts}per sign-in so each session has a distinct key (NodeUserAuthorization.signIn).get_did(id)→ thedid:key…#…URL;jwk(id)→ the serialized private JWK;list_session_keys().add_targeted_actions(target, actions)/build(config, …)— accumulate ReCap abilities and emit the SIWE string the wallet signs.
The Ed25519 signing of sub-delegations and invocations happens in Rust/WASM (the upstream tinycloud-sdk-wasm Session); for raw encryption-network invocations the audience is re-signed in JS against the session JWK (rewriteInvocationAudience, see SDK map). The private key never crosses to the server — only the resulting signed UCAN / CACAO does.
Where it sits in sign-in
At sign-in the SDK: generates the session key → builds a SIWE-ReCap message naming the requested abilities with the did:key as delegatee → the wallet signs it → completeSessionSetup mints the session UCAN delegation header. From then on the session key is the principal the node sees.
Relationships
A session key is a did:key DID; it is the audience of the owner's root capability delegation (granted in SIWE/CACAO carrying a ReCap); it issues UCAN sub-delegations and invocations; it is generated/managed by the WASM session manager invoked during the sign-in-flow; its reach is bounded by attenuation; it is produced so apps never hold the owner key minted by OpenKey.
Example
Signing in to a notes app: the SDK generates did:key:z6MkpTHR… (renamed session-1718000000). The owner did:pkh:eip155:1:0xf39F…2266 signs one SIWE message delegating tinycloud.kv/{get,put,list,del,metadata} over tinycloud:pkh:eip155:1:0xf39F…2266:default/kv/ to that did:key. The app now signs each tinycloud.kv/put invocation with the session key; the wallet is never prompted again until the session expires. (See sign-in-flow, capabilities.)
Status & drift
Shipped. The session manager generates a fresh Ed25519 key per sign-in and is the only place app-side signing keys live. Note the did:key audience is case-sensitive (dids) — session-key matching does not checksum-canonicalize the way did:pkh does, so the exact base58 string must round-trip.
Sources
js-sdk:packages/sdk-rs/src/session/manager.rs(SessionManager/TCWSessionManager: key gen,get_did,jwk,build,add_targeted_actions),packages/sdk-core/src/identity.ts(did:keycanonicalization),packages/node-sdk/src/authorization/NodeUserAuthorization.ts(renameSessionKeyId, session expiry clamping)