Skip to content

Conversation

@SHAcollision
Copy link
Contributor

@SHAcollision SHAcollision commented Nov 27, 2025

  • Introduced shared PublicKey/Keypair wrappers in pubky-common::crypto::keys so every crate displays identifiers as pubky<z32> by default while still exposing .z32() for raw hostnames and registry usage.
  • For now we go with pubky... as default prefix for the public key identifier. This will be easy to change if the product team decides otherwise.
  • Replaced remaining direct pkarr key usages across the workspace (SDK, JS bindings, homeserver, testnet) with the shared wrappers to guarantee consistent formatting and serde behavior. The pkarr-republisher keeps using pkarr keys.
  • Removed the re-exported pkarr keys, instead it now re-export pubky keys from pubky-sdk, keeping documentation and examples aligned with the new display defaults.

@ok300
Copy link
Contributor

ok300 commented Nov 28, 2025

Nice, I was about to look into a similar PR, glad this is already in place.

Can you please add a way to get from a Keypair to a PubkyId?

For example:

impl Keypair {
    fn pubky_id(&self) -> Result<PubkyId, String> {
        PubkyId::try_from(self.public_key().to_z32().as_str())
    }
}

which allows

// Before (verbose)
let pubky_id = PubkyId::try_from(&keypair.public_key().to_z32())?;

// After (concise)
let pubky_id = keypair.pubky_id()?;

@SHAcollision
Copy link
Contributor Author

SHAcollision commented Nov 28, 2025

Nice, I was about to look into a similar PR, glad this is already in place.

Can you please add a way to get from a Keypair to a PubkyId?

For example:

impl Keypair {
    fn pubky_id(&self) -> Result<PubkyId, String> {
        PubkyId::try_from(self.public_key().to_z32().as_str())
    }
}

which allows

// Before (verbose)
let pubky_id = PubkyId::try_from(&keypair.public_key().to_z32())?;

// After (concise)
let pubky_id = keypair.pubky_id()?;

This would introduce a dependency on pubky-app-specs here, right? Can we do it without this dependency? I don't think we want that under any circumstance. Instead we likely want a method in pubky-app-specs that can From a PubkyId from a pubky::PublicKey ?

@SHAcollision
Copy link
Contributor Author

@SeverinAlexB Marking as ready for review here. No rush:

  • There is a small conflict with the changes introduced in feat(sdk): added auth signup flow #268 . When this conflict is solved, tests fail: most likely we need to use the new stringification methods correctly on the new AuthFlow but needs some research.
  • Warning AI slop: there are large parts of this PR that have been fixed by AI agents (e2e tests, etc). I haven't dived into all changes line by line. A more conscious work to verify the whole PR is yet to be done, unsafe to merge without it!
  • Feel free to push commits here and make changes as you like, It's good to me if you want to take ownership of it and merge it when it's OK according to your view. That's much preferable to having it stale and possibly even block v0.6.0 release until January. Feel free to delegate to someone else as well!

@SHAcollision SHAcollision marked this pull request as ready for review December 3, 2025 15:28
Copy link
Collaborator

@SeverinAlexB SeverinAlexB left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is good. No idea if this will lead to accidental breaking changes in the homeserver.

Does the new KeyPair/PublicKey have the same lib export path as the old one? Maybe it is better to export it in a different path so one needs to explicitly upgrade to the new KeyPair/PublicKey so the dev can check each case.

@SHAcollision
Copy link
Contributor Author

SHAcollision commented Dec 15, 2025

Does the new KeyPair/PublicKey have the same lib export path as the old one? Maybe it is better to export it in a different path so one needs to explicitly upgrade to the new KeyPair/PublicKey so the dev can check each case.

yeha, good question. The wrapper keep the same public export locations (pubky_common::crypto::Keypair/PublicKey, re‑exported via pubky::Keypair/PublicKey) , downstream code continues to use the existing paths. The change is behavioral (prefixed display) rather than a rename. Consumers will now be using pubky:: keys instead of pkarr:: keys so indeed they are different types on the same path, yet I believe it's correct to fully delete the previous pkarr keys export and replace tat path with the new type since pkarr:: keys are of no use at all because no function on SDK / Homeserver accepts them anymore.

Overall, 0.6.0 vs previous 0.5.4 upgrade (big breaking change) leads consumers to fully re-do their use of the crate, and this is probably one of the least impactful changes.

@SHAcollision
Copy link
Contributor Author

SHAcollision commented Jan 2, 2026

Merged main and fixes a myriad of conflicts and problems that rose from working in paralell with the new signup/signin authflow:

  • Pubky key wrappers wired through auth flow + deep link signup (hostnames use .z32() now) in auth_flow.rs and signup.rs.
  • Session export/import now compiles on native with a non‑WASM import guard in core.rs; native prepare_request added in native.rs.
  • JS bindings aligned to wrapper API in keys.rs and pkarr cache seeding uses wrapper conversion in http.rs.
  • Deterministic SQL ordering for list operations. I'm getting different ordering from deep list test local vs github runner. I losen the test to not expect some exact ordering. Needs more research.
  • Metrics test now uses z32 host strings in metrics.rs; macro doctest example ignored in lib.rs.
  • Tightened prefix handling in keys.rs to avoid misparsing raw keys that start with pubky.
  • Switched homeserver URL helpers back to .z32() for raw pkarr-style URLs.

@SHAcollision SHAcollision force-pushed the feat/pubky-keys branch 2 times, most recently from 1510562 to cecdc58 Compare January 8, 2026 07:55
Copy link
Collaborator

@SeverinAlexB SeverinAlexB left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few comments but I think most of it is good.

Did you ever close the prod or staging database and tested it with the data there to find issues?

Comment on lines +117 to +119
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct PublicKey(pkarr::PublicKey);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Review which I agree with

CleanShot 2026-01-09 at 09 46 47@2x

Comment on lines +61 to +63
if is_prefixed_pubky(s) {
continue;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I see this correctly that the new pubky prefixed keys are not supported in pubky_host?

Comment on lines +158 to +164
impl Deref for PublicKey {
type Target = pkarr::PublicKey;

fn deref(&self) -> &Self::Target {
&self.0
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Review. Interesting food for thought. Not sure what's best here.

CleanShot 2026-01-09 at 09 52 00@2x

Comment on lines +11 to +14
/// Returns true if the value is in `pubky<z32>` form.
pub fn is_prefixed_pubky(value: &str) -> bool {
matches!(value.strip_prefix("pubky"), Some(stripped) if stripped.len() == 52)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: Shouldn't this be a static method that belongs to the PublicKey like PublicKey::is_pubky_prefixed(key: &str)?

Comment on lines +36 to +42
/// Export the secret key bytes.
#[must_use]
pub fn secret_key(&self) -> [u8; 32] {
let mut out = [0u8; 32];
out.copy_from_slice(self.0.secret_key().as_ref());
out
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory, this is the secret aka seed and not the secret_key aka private key. I know pkarr is already naming it this way but should we fix the naming here at least?

There are obviously other methods that would need to be renamed.

CleanShot 2026-01-09 at 10 05 39@2x

Comment on lines +16 to +23
fn parse_public_key(value: &str) -> Result<pkarr::PublicKey, ParseError> {
let raw = if is_prefixed_pubky(value) {
value.strip_prefix("pubky").unwrap_or(value)
} else {
value
};
pkarr::PublicKey::try_from(raw.to_string())
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: Similar to the is_prefixed_pubky, shouldn't this be a static method on PublicKey?

Comment on lines +123 to +138
#[must_use]
pub const fn as_inner(&self) -> &pkarr::PublicKey {
&self.0
}

/// Extract the inner [`pkarr::PublicKey`].
#[must_use]
pub fn into_inner(self) -> pkarr::PublicKey {
self.0
}

/// Return the raw z-base32 representation without the `pubky` prefix.
#[must_use]
pub fn z32(&self) -> String {
self.0.to_string()
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing you need to change, just a comment:

I find the introduction of the #[must_use] tag really weird. For me, this feels like it clutters the code unnecessarily. Feels like a rust language downgrade.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants