# Improving l1 to l2 messaging API

**URL:** https://forum.aztec.network/t/improving-l1-to-l2-messaging-api/8600
**Category:** General
**Created:** [June 26, 2026, 8:30am UTC](https://forum.aztec.network/t/improving-l1-to-l2-messaging-api/8600 "2026-06-26T08:30:41Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![benesjan](https://dub1.discourse-cdn.com/flex013/user_avatar/forum.aztec.network/benesjan/32/39_2.png) [@benesjan](https://forum.aztec.network/u/benesjan)
#### Post date: [June 26, 2026, 8:30am UTC](https://forum.aztec.network/t/improving-l1-to-l2-messaging-api/8600/1 "2026-06-26T08:30:41Z")

</div>

**Update** : Moved this post to Aztec Governance [here](https://github.com/AztecProtocol/governance/discussions/52).

In Oxide we don’t really use the secret hash when depositing to L2 (it’s set to a constant value everywhere) and that is because we want the message to contain a recipient. So what we do instead is that we have `recipient_hash` that is computed offchain as `hash(secret, recipient)`. Then this recipient hash is part of the message content preimage.

(The secret in the recipient hash essentially just acts as a randomness hiding recipient from preimage attacks)

This has some nice properties:

- It makes the security of messages the same as notes: “recipient” in the message is the same thing as note owner. It’s no longer only the secret that “owns” the message,
- it allows someone else to claim a message on your behalf (e.g. a relayer) - but this this doesn’t seem to be that relevant anymore as oxide’s lazy claiming approach deprecates this (mentioning it here for completness).

This has some very **terrible properties** for Oxide because with constant secret **anyone can see when a recipient spends the message** (broken nullifier unlinkability property):

```rust
pub fn compute_l1_to_l2_message_nullifier(message_hash: Field, secret: Field) -> Field {
    poseidon2_hash_with_separator([message_hash, secret], DOM_SEP__MESSAGE_NULLIFIER)
}

```

(Note that we could work around this by not using Aztec.nr’s `process_l1_to_l2_message` but I would rather refactor it all.)

### Possible solutions

**Solution 1** : Drop the secret hash altogether and just expect the contract calling Inbox to handle it and then have standardized deposit and claim utils in Solidity and in Aztec.nr.

**Solution 2** : Replace `secret_hash` with `salted_recipient_hash` in the Inbox contract and everywhere and then we modify `compute_l1_to_l2_message_nullifier` function to use recipient’s `nhk` instead of the secret.

I think solution 1 is better because with the utils we can get basically equivalent devex without enshrining a specific derivation scheme in l1 contracts.

Opinions?

---

<div class="post-metadata">

### Author: ![leila](https://dub1.discourse-cdn.com/flex013/user_avatar/forum.aztec.network/leila/32/2335_2.png) [@leila](https://forum.aztec.network/u/leila)
#### Post date: [June 26, 2026, 9:56am UTC](https://forum.aztec.network/t/improving-l1-to-l2-messaging-api/8600/2 "2026-06-26T09:56:06Z")

</div>

I think having a message hash and a secret hash is a nice separation.

The secret in the message is for hiding what the message does. In our case it’s the recipient. But it could also has no secret and be claimable for whoever claims it first (or with some conditions checked in the app, e.g. anyone that can prove they are over 70 years old).

And the secret in the secret hash is for hiding the fact that a nullifier has been created. An app might choose to make it constant so that anyone can see that it has been claimed.

So in oxide, maybe we just need to actually use a secret instead of a constant for the secret hash?

---

<div class="post-metadata">

### Author: ![sirasistant](https://dub1.discourse-cdn.com/flex013/user_avatar/forum.aztec.network/sirasistant/32/2973_2.png) [@sirasistant](https://forum.aztec.network/u/sirasistant)
#### Post date: [June 29, 2026, 5:51am UTC](https://forum.aztec.network/t/improving-l1-to-l2-messaging-api/8600/3 "2026-06-29T05:51:08Z")

</div>

I think option one is better. I think the inbox is forcing a consumption pattern on portals (consume it via providing the preimage to a hashed secret, and nullify using the preimage), but the inbox should not enforce this, it should be a problem for the portal to solve (how to consume, and how to nullify is portal-specific)  
Everything that @leila mentioned above can be implemented by the portal itself in an app specific manner even if we remove the secret hash from the inbox if i’m not mistaken.

---

<div class="post-metadata">

### Author: ![benesjan](https://dub1.discourse-cdn.com/flex013/user_avatar/forum.aztec.network/benesjan/32/39_2.png) [@benesjan](https://forum.aztec.network/u/benesjan)
#### Post date: [June 29, 2026, 10:51am UTC](https://forum.aztec.network/t/improving-l1-to-l2-messaging-api/8600/4 "2026-06-29T10:51:41Z")

</div>

> Everything that @leila mentioned above can be implemented by the portal itself in an app specific manner even if we remove the secret hash from the inbox if i’m not mistaken.

Correct. Inbox doing the minimum necessary seems like a very good pattern 👍

---

<div class="post-metadata">

### Author: ![Mike](https://dub1.discourse-cdn.com/flex013/user_avatar/forum.aztec.network/mike/32/7_2.png) [@Mike](https://forum.aztec.network/u/Mike)
#### Post date: [June 29, 2026, 3:16pm UTC](https://forum.aztec.network/t/improving-l1-to-l2-messaging-api/8600/5 "2026-06-29T15:16:09Z")

</div>

If we were starting again, I imagine we’d adopt the abstract approach of Option 1, where portals (and their L2 counterparts) figure out how to create a nullifier for a consumed L1-\>L2 msg.

Today, there are lots of example libraries (and the FeeJuice contract!) which already use the existing secretHash rails and libraries, so it would be more work [1] to update the interface, and more risk of introducing bugs into the FJ contract.

It seems there’s an Option 3:

Leave the protocol aspects of L1-\>L2 messaging alone, and create \>=2 variants of `compute_l1_to_l2_message_nullifier` for different use cases, in much the same way as notes can be nullified in custom ways.

E.g. you could have both of these variants:

- a) `compute_l1_to_l2_message_nullifier_with_secret_hash` - an alias for today’s `compute_l1_to_l2_message_nullifier`
- b) `compute_l1_to_l2_message_nullifier_with_nullifier_hiding_secret_key` - which assumes, for example, the first field in the preimage of the `message_hash` (`message_hash ===` the `content` of the L1-\>L2 message) represents an “owner” whose nhsk must be used in computing the nullifier.

With (b), you achieve what you want: you get a nullifier that doesn’t leak which L1-\>L2 msg is being consumed, and you don’t need to change the protocol. The `secret_hash` is ignored with variant (b).

* * *

Aside:  
`recipient` is already a term in L1-\>L2 message parlance which means “the contract address which contains the function that will consume this message” or equivalently “the contract address which will be ‘siloed’ with the consumption nullifier of this message”.  
You’re overloading `recipient` (with the name `recipient_hash`) to also mean “the person whose nhsk will be used to compute the nullifier for the message”.  
I’d suggest at least renaming your `recipient_hash` to something else to make this conversation (and the code) easier to follow. `owner` might be a good term, since that aligns with the term used for “the person who possesses the nhsk to nullify a note”.

* * *

[1] If you still want to adopt your “Option 1”, I would suggest the “definition of done” should be:

- The protocol is updated, end-to-end, including the FeeJuice contract.
- Prominent ecosystem libraries (incl Wonderland’s libraries) which do L1-\>L2 messaging “the old way” are _also_ all updated to work with the updated protocol.

---

<div class="post-metadata">

### Author: ![benesjan](https://dub1.discourse-cdn.com/flex013/user_avatar/forum.aztec.network/benesjan/32/39_2.png) [@benesjan](https://forum.aztec.network/u/benesjan)
#### Post date: [June 30, 2026, 6:02am UTC](https://forum.aztec.network/t/improving-l1-to-l2-messaging-api/8600/6 "2026-06-30T06:02:55Z")

</div>

@Mike Nico answered on the [governance post](https://github.com/AztecProtocol/governance/discussions/52#discussioncomment-17475073) and I think the solution of:

> I think a simpler and better change is to simply rename content to public\_content and secret to private\_content, resulting in messages having public\_content\_hash and private\_content\_hash.

is nice.

From the L1 contract’s PoV it would literally be just a rename of arguments.
