What should you return to the UI: HeaderHash or HeaderHashB64?

I am trying to figure out what the best practice is for returning hashes to your UI. At this point I have 2 options:

1/ HeaderHash

This Rust object is returned as a bytearray/buffer to the frontend. But if you call .toString() on it you will get a something that is not a base64 url: e.g. �Z{���s�lI\���9��H
Also using URLSafeBase64.encode(myHeaderHash); will not work, because holochain prepends hashes with a “u”.
This is a working implementation holochain-open-dev/core-types

2/ HeaderHashB64

This is a simple string, is correctly serialized already inside your WASM zome. And can be used in your frontend code in dictionaries/maps.

So I am wondering if there is ever a case where you would prefer to sending a HeaderHash over a HeaderHashB64 to the frontend?

Any suggestions?

1 Like

So there’s a couple different options around serialization that I’ve seen…

Some handle serialization on the client side, I’m not a big fan although sometimes it seems unavoidable, given that when you use the @holochain/conductor-api certain calls reply with a CellId / DnaHash / AgentHash etc.

There is a repo you may or may not know of that Holo uses for its own projects for handling client side serialization called cryptolib :s


It’s not published to npm so you have to reference it via github as a node/npm dependency.

For handling serialization I worked out a way to wrap hash references in something that serializes automatically to and from a string on the Rust side… the pattern got picked up and included in another holochain library called hc-utils, which you can use…
Here’s a file https://github.com/holochain/hc-utils/blob/develop/crates/hc-utils/src/wrappers.rs
The magic is really in

#[serde(try_from = "HashString")]
#[serde(into = "HashString")]

which defines a native conversion for going over and back from the WASM guest/host (holochain).

Anywhere you used AgentPubKey in some struct you just swap for WrappedAgentPubKey and now it goes over the wire fine, and your client side plays nicer with it, and doesn’t have to think serialization.

1 Like

So @Connoropolous just to let you know, we realized after using the wrappers for while that the HoloHashB64 primitives found in holo_hash (which hdk depends on) already serialize to strings, in the same way. You can still use the wrapped structs, but I’ve found it better not to depend on the changing hc_utils on my happs, and instead depend on the already there holo_hash.

Also this may be moved to the hdk: https://github.com/holochain/holochain/issues/682

Thanks Guillem, I don’t actually like depending on a lib like hc-utils with holochain either because of the need for intermittent updating, and Holochain isn’t reliable on that piece at the moment. So I have the wrappers in acorn src code as well. Can you cite an example of someone/somewhere using HolochashB64?

1 Like

Sure, all my stuff basically does that (compository and holochain-open-dev). You can see a simple example here: https://github.com/holochain-open-dev/file-storage/blob/main/crates/file_storage_zome/src/lib.rs.

I see the use of it there, makes sense. In the simple ‘returning a hash’ case it’s nice and explicit and makes sense. You’re not trying to return a struct. When returning a struct over the boundary it’s messier because then the whole struct needs to be converted to a struct that’s similar/equivalent except that it’s hashes are b64 strings. I see in compository you still have WrappedHeaderHash, etc, but hasn’t been updated since December. You had a “TODO: fix this” on that so I’m curious what you’d do differently.

I find it’s nice not to have to think about catching each individual case of transforming to b64 for serialization, which is why the

#[serde(try_from = "HashString")]
#[serde(into = "HashString")]

is really nice

Mmm I’m not sure exactly what you mean with serialiizing the whole entry… I’m also using these types inside structs, like this: file-storage/lib.rs at main · holochain-open-dev/file-storage · GitHub.

I’ve found they work almost exactly the same way that the wrapped helpers, the only difference is how you convert from those types to “normal” types like EntryHash, which in the case of *B64 is with into() and from().

Does this address your comment? Not sure :slight_smile:

Oh yeah, I didn’t see that one. Got it, very similar.
I notice right above you use AgentPubKey but maybe you don’t need that one string-ified.

1 Like