Skip to main content

Crate pubky

Crate pubky 

Source
Expand description

§Pubky SDK

Ergonomic building blocks for Pubky apps: one facade (Pubky) plus focused actors for sessions, storage API, signer helpers, and QR auth flow for keyless apps.

Rust implementation of Pubky SDK.

§Install

# Cargo.toml
[dependencies]
pubky = "0.x"            # this crate
# Optional helpers used in examples:
# pubky-testnet = "0.x"

§Quick start

use pubky::prelude::*;
use pubky::ClientId;


let pubky = Pubky::new()?; // or Pubky::testnet() for local testnet.

// 1) Create a new random key user and bound to a Signer
let keypair = Keypair::random();
let signer = pubky.signer(keypair);

// 2) Sign up on a homeserver (identified by its public key)
let homeserver = PublicKey::try_from("o4dksf...uyy").unwrap();
signer.signup(&homeserver, None).await?;
let session = signer.signin(ClientId::new("my-cool-app").unwrap()).await?;

// 3) Read/Write as the signed-in user
session.storage().put("/pub/my-cool-app/hello.txt", "hello").await?;
let body = session.storage().get("/pub/my-cool-app/hello.txt").await?.text().await?;
assert_eq!(&body, "hello");

// 4) Public read of another user’s file
let txt = pubky
    .public_storage()
    .get(format!(
        "{}/pub/my-cool-app/hello.txt",
        session.info().public_key()
    ))
    .await?
    .text().await?;
assert_eq!(txt, "hello");

// 5) Keyless app flow (QR/deeplink)
let caps = Capabilities::builder()
    .write("/pub/example.com/")
    .expect("static scope is canonical")
    .finish();
let flow = pubky.start_cookie_auth_flow(&caps, AuthFlowKind::signin())?;
println!("Scan to sign in: {}", flow.authorization_url());
let app_session = flow.await_approval().await?;

// 6) Optional (advanced): publish or resolve PKDNS (_pubky) records
signer.pkdns().publish_homeserver_if_stale(None).await?;
let resolved = signer.pkdns().get_homeserver().await;
println!("Your current homeserver: {:?}", resolved);

§Key formats (display vs transport)

PublicKey has two string representations:

  • Display format: pubky<z32> (used for logs/UI and human-facing identifiers).
  • Transport/storage format: raw z32 (used for hostnames, headers, query params, serde/JSON, and database storage).

Use .z32() whenever you are building hostnames or transport values (for example _pubky.<z32> or the pubky-host header). Use Display/.to_string() when you want the prefixed identifier for people.

§Reuse a single facade across your app

Use a shared Pubky (via cloning it, passing down as argument or behind OnceCell) instead of constructing one per request. This avoids reinitializing transports and keeps the same client available for repeated usage.

§Mental model

  • Pubky - facade, always start here! Owns the transport and constructs actors.
  • PubkySigner - local key holder. Can signup, signin, approve QR auth, publish PKDNS.
  • PubkySession - authenticated “as me” handle. Exposes session-scoped storage.
  • GrantManager - account-level grant listing and revocation using an authenticated root session.
  • PublicStorage - unauthenticated reads of others’ public data.
  • Pkdns - resolve/publish _pubky records.
§Transport:
  • PubkyHttpClient : handles requests to pubky public-key hosts.

§Examples

§Storage API (session & public)

Session (authenticated):

use pubky::{ClientId, Keypair, Pubky};


let pubky = Pubky::new()?;
let session = pubky
    .signer(keypair)
    .signin(ClientId::new("my-cool-app").unwrap())
    .await?;

let storage = session.storage();
storage.put("/pub/my-cool-app/data.txt", "hi").await?;
let text = storage.get("/pub/my-cool-app/data.txt").await?.text().await?;

Public (read-only):

use pubky::{Pubky, PublicKey};


let pubky = Pubky::new()?;
let public = pubky.public_storage();

let file = public
    .get(format!("{user_id}/pub/example.com/file.bin"))
    .await?
    .bytes()
    .await?;

let entries = public
    .list(format!("{user_id}/pub/example.com/"))?
    .limit(10)
    .send()
    .await?;
for entry in entries {
    println!("{}", entry.to_pubky_url());
}

See the Public Storage example.

Path rules:

  • Session storage uses absolute paths under /pub/ or /priv/.
  • Public storage uses addressed form pubky<user>/pub/app/file.txt (preferred) or pubky://<user>/....
  • A storage path cannot be both an exact file and an implicit folder prefix. For example:
    • if /pub/app/foo exists, writing /pub/app/foo/bar.json returns 409 Conflict.
    • if descendants under /pub/app/foo/ exist, writing /pub/app/foo returns 409 Conflict.

Convention: put your app’s public data under a domain-like folder in /pub, e.g. /pub/my-new-app/.

See Private Storage for /priv/ access and events.

§Resolve identifiers into transport URLs

Need to feed a public resource into a raw HTTP client? Use resolve_pubky to transform the human-facing identifier into the HTTPS homeserver URL:

let url = resolve_pubky("pubkyoperrr8wsbpr3ue9d4qj41ge1kcc6r7fdiy6o3ugjrrhi4y77rdo/pub/pubky.app/posts/0033X02JAN0SG")?;
assert_eq!(
    url.as_str(),
    "https://_pubky.operrr8wsbpr3ue9d4qj41ge1kcc6r7fdiy6o3ugjrrhi4y77rdo/pub/pubky.app/posts/0033X02JAN0SG"
);

§PKDNS (Pkarr)

Resolve another user’s homeserver (_pubky record), or publish your own via the signer.

use pubky::{Pubky, PublicKey, Keypair};
let pubky = Pubky::new()?;

// read-only homeserver resolver
let host: Option<PublicKey> = pubky.get_homeserver_of(&other).await?;

// publish with your key
let signer = pubky.signer(Keypair::random());
signer.pkdns().publish_homeserver_if_stale(None).await?;
// or force republish (e.g. homeserver migration)
signer.pkdns().publish_homeserver_force(Some(&new_homeserver_id)).await?;
// resolve your own homeserver
signer.pkdns().get_homeserver().await?;

§Pubky QR auth for third-party and keyless apps

Request an authorization URL and await approval.

Typical usage:

  1. Start an auth flow with pubky.start_grant_auth_flow(&caps, ..) (or pubky.start_cookie_auth_flow(&caps, ..) for the cookie variant). You can also use PubkyGrantAuthFlow::builder() / PubkyCookieAuthFlow::builder() to set a custom relay.
  2. Show authorization_url() (QR/deeplink) to the signing device (e.g., Pubky RingiOS / Android).
  3. Await await_approval() to obtain a session-bound PubkySession, or await_credential() for a raw GrantCredential/CookieCredential that you can persist, inspect, or lift into a session later via PubkySession::from_{grant,cookie}_credential.

let pubky = Pubky::new()?;
// Read/Write capabilities for acme.app route
let caps = Capabilities::builder()
    .read_write("/pub/example.com/")
    .expect("static scope is canonical")
    .finish();

// Start the flow using the default relay (see “Relay & reliability” below)
let flow = pubky.start_cookie_auth_flow(&caps, AuthFlowKind::signin())?;
println!("Scan to sign in: {}", flow.authorization_url());

// On the signing device, approve with: signer.approve_auth(flow.authorization_url()).await?;

let session = flow.await_approval().await?;

Optional x-callback parameters let the authenticator return control to the requesting app after it has handled the deep link. They work with both cookie and grant flows:

let caps = Capabilities::builder()
    .read_write("/pub/example.com/")
    .expect("static scope is canonical")
    .finish();
let callbacks = XCallbackParams {
    x_source: Some("Example App".into()),
    x_success: Some("example://auth/success?nonce=unique".into()),
    x_error: Some("example://auth/error?nonce=unique".into()),
    x_cancel: Some("example://auth/cancel?nonce=unique".into()),
};

let flow = PubkyGrantAuthFlow::builder(
    &caps,
    AuthFlowKind::signin(),
    ClientId::new("example.com").expect("static client id is valid"),
)
.x_callback(callbacks)
.start()?;

Approve an auth request

signer.approve_auth(authorization_url).await?;

See the fully functional Auth Flow Example.

§Relay & reliability
  • If you don’t specify a relay, the auth flow defaults to a Synonym-hosted relay. If that relay is down, logins won’t complete.
  • For production and larger apps, run your own relay (MIT, Docker): https://httprelay.io. The channel is derived as base64url(hash(secret)); the token is end-to-end encrypted with the secret and cannot be decrypted by the relay.

Custom relay example

let pubky = Pubky::new()?;
let caps = Capabilities::builder()
    .read("/pub/example.com/")
    .expect("static scope is canonical")
    .finish();
let client_id = ClientId::new("my.app").unwrap();
let auth_flow = PubkyGrantAuthFlow::builder(&caps, AuthFlowKind::signin(), client_id)
    .client(pubky.client().clone())
    .relay(url::Url::parse("http://localhost:8080/inbox/")?) // your relay
    .start()?;

Tip: reuse pubky.client() when customising the relay so the flow shares TLS and pkarr configuration with the rest of your application.

§Features

  • json: enable Storage helpers (.get_json() / .put_json()) and serde on certain types.
# Cargo.toml
[dependencies]
pubky = { version = "x.y.z", features = ["json"] }

§Testing locally

Spin up an ephemeral testnet (DHT + homeserver + relay) and run your tests fully offline:


let testnet = EphemeralTestnet::builder().build().await.unwrap();
let homeserver  = testnet.homeserver_app();
let pubky = testnet.sdk()?;

let signer = pubky.signer(Keypair::random());
signer.signup(&homeserver.public_key().into(), None).await?;
let session = signer
    .signin(ClientId::new("my-cool-app").unwrap())
    .await?;

session.storage().put("/pub/my-cool-app/hello.txt", "hi").await?;
let s = session.storage().get("/pub/my-cool-app/hello.txt").await?.text().await?;
assert_eq!(s, "hi");

§Keypair and Session persistence

Encrypted Keypair secrets (.pkarr):

use pubky::Pubky;
let pubky = Pubky::new()?;
let signer = pubky.signer_from_recovery_file("/path/to/alice.pkarr", "passphrase")?;

Session secrets (.sess):

use pubky::{ClientId, Keypair, Pubky};
let pubky = Pubky::new()?;
let keypair = Keypair::random();
let session = pubky
    .signer(keypair)
    .signin(ClientId::new("my-cool-app").unwrap())
    .await?;
session.write_secret_file("alice.sess").unwrap();
let restored = pubky.session_from_file("alice.sess").await?;

Security: the .sess secret is a bearer token. Anyone holding it can act as the user within the granted capabilities. Treat it like a password.

§Example code

Check more examples using the Pubky SDK.

§JS bindings

Find a wrapper of this crate using wasm_bindgen in npmjs.com. Or build on pubky-sdk codebase under pubky-sdk/bindings/js.


License: MIT Relay: https://httprelay.io (open source; run your own for production)

Modules§

deep_links
Deep link related module. Contains the following:
errors
Unified error types for the pubky crate.
pkarr
Pkarr
prelude
Common imports for quick starts.
recovery_file
Tools for encrypting and decrypting a recovery file storing user’s root key’s secret.

Macros§

cross_log
Cross-platform logging macro with explicit level selection.

Structs§

AuthToken
Authentication token used by the Pubky Auth protocol.
Capabilities
A wrapper around Vec<Capability> that controls how capabilities are serialized and built.
Capability
A single capability: a scope and the allowed actions within it.
ClientId
An application identifier, typically a domain string (e.g., franky.pubky.app).
CookieCredential
Cookie-based session credential (legacy).
CookieSessionRecord
Cookie-specific session record with binary (postcard) serialization.
CookieSessionView
Cookie-only operations on a PubkySession.
DelegatedGrantAuthFlowState
Serializable state for resuming a pending delegated browser grant auth flow.
DelegatedGrantCredentialState
Non-secret durable metadata for browser delegated grant restore.
EncryptedHttpRelayInboxChannel
An encrypted HTTP relay inbox channel that encrypts/decrypts messages using a shared secret, with store-and-forward semantics.
Event
A single event from the event stream.
EventCursor
Cursor for pagination in event queries.
EventStreamBuilder
Builder for creating an event stream subscription.
GrantAuthFlowState
Serializable state for resuming a pending grant auth flow.
GrantClaims
Grant JWS claims — the serializable JWS claims.
GrantCredential
Cheap-to-clone grant credential. The mutable token state is shared across clones via Arc<Mutex<…>> so every PubkySession clone observes the same refreshes. Session info is derived from the immutable grant and never changes.
GrantInfo
Summary of an active grant returned by GET /auth/grant/sessions.
GrantManager
Account-level grant management for a signed-in user.
GrantSessionInfo
Session metadata returned alongside the bearer.
GrantSessionResponse
Response from POST /session for grant-based authentication.
GrantSessionView
grant-only operations on a PubkySession.
HttpRelayInboxChannel
An HTTP relay inbox channel for store-and-forward messaging.
Keypair
Wrapper around pkarr::Keypair that customizes PublicKey rendering.
ListBuilder
Unified builder for homeserver LIST queries (works for session & public).
Method
The Request Method (VERB)
Pkdns
PKDNS actor: resolve & publish _pubky PKARR records.
PopProofClaims
Proof-of-Possession JWS claims — the serializable JWS claims.
Pubky
High-level facade — your entry point to the Pubky SDK.
PubkyCookieAuthFlowDeprecated
End-to-end legacy (cookie) auth flow handle.
PubkyGrantAuthFlow
End-to-end Grant + PoP auth flow handle.
PubkyHttpClient
Transport client for Pubky homeserver APIs and generic HTTP, with PKARR-aware URL handling.
PubkyHttpClientBuilder
Configures a PubkyHttpClient before construction.
PubkyResource
An addressed resource: (owner: PublicKey, path: ResourcePath).
PubkySession
Your authenticated handle after signing in.
PubkySigner
Holds a private key and proves identity.
PublicKey
Wrapper around pkarr::PublicKey that renders with the pubky prefix.
PublicStorage
Read anyone’s public data without signing in (unauthenticated).
ResourcePath
An absolute, URL-safe homeserver path (/…), with percent-encoding where needed.
ResourceStats
Typed metadata for a stored resource, extracted from HEAD response headers.
SessionInfo
Minimal, auth-agnostic session metadata.
SessionStorage
Read and write your own data with simple path-based operations (authenticated).
StatusCode
An HTTP status code (status-code in RFC 9110 et al.).
StoragePath
A canonical decoded Pubky storage path that can be encoded as an HTTP/WebDAV URI path.

Enums§

AuthFlowKind
The kind of authentication flow to perform.
BuildError
Errors that can occur while building a crate::PubkyHttpClient.
Error
The crate’s top-level error type.
EventType
Type of event in the event stream.
StoragePathError
Error validating or normalizing a StoragePath.

Constants§

DEFAULT_HTTP_RELAYDeprecated
Default HTTP relay base when none is supplied.
DEFAULT_HTTP_RELAY_INBOX
Default HTTP relay inbox base when none is supplied.
DEFAULT_STALE_AFTER
Default staleness window for homeserver _pubky Pkarr records (1 hour).
GRANT_JWS_TYP
JWS header typ for Grant tokens.
POP_JWS_TYP
JWS header typ for Proof-of-Possession proofs.

Traits§

IntoPubkyResource
Convert common input types into a normalized, addressed PubkyResource.
IntoResourcePath
Convert common input types into a normalized ResourcePath (absolute).

Functions§

resolve_pubky
Resolve a Pubky identifier (either pubky:// or pubky<pk>/…) into a transport URL.

Type Aliases§

GrantId
Grant identifier — a RandomId used as the jti claim in a Grant JWS.
PopNonce
Proof-of-Possession nonce — a RandomId used to prevent PoP replay.
Result
A specialized Result type for pubky operations.