Proposal - zkAccess: Zero-knowledge access layer on top of Ceramic / ComposeDB

Grant/Project Name: zkAccess

Proposer’s contact info: Jose Aguinaga (

Grant Category: Apps

ELI5 Project Summary: An access manager where people can get a “ticket” to enter a particular location or venue, without revealing any information about where they got that “ticket”.

Project Description: The grant is to develop zkAccess, an application which leverages on ComposeDB and Zkp-ECDSA (by Cloudflare), where every individual stores a ECDSA P-256 public keypair in their own profile. These keypairs can be “merged” into a single composite representing an event, which then can be verified for gatekeepers to check. Owners of the “merged” keypairs can create ZkAttest proofs offline and share it to invitees or for personal use to have access to the event, w/o disclosing who they are or who invited them.

Architecture: The app relies in two main data models: a public key per user, and a mapping of multiple public keys per app. This allows querying multiple users from various applications assuming the application wants to grant access to these users. Within the app we have multiple roles: guests, looking to “access” a venue or system; hosts, individuals able to issue the “access”; venues, apps or places managing hosts and finally guards, which review the proofs/tickets issued by hosts and granted to guests.

Relevant links:

  • Website: jjperezaguinaga[dot]com
  • Twitter: twitter[dot]com/0xjjpa
  • Github: github[dot]com/0xjjpa
  • Previous projects the team has meaningfully contributed to: Onchecked (, conFIEL (
  • Comparable projects or peers: Sismo (sismo[dot]io/)

Ceramic Ecosystem Value Proposition:

  • Generate a database of public keys of individuals connected to DIDs to slowly make Ceramic the de factor auth layer for private applications.
  • Increase adoption of DID standards by leveraging cutting edge technologies (e.g. webauthn) to provide easy key management over existing solutions.
  • Bring awareness of the indexing and composable capabilities of the Ceramic network and provide an authentication layer within the decentralized ecosystem

In general we are looking to build the Auth0 of decentralized applications by bringing SSO-like access capabilities to DApps using Ceramic as the “auth” table. The added value is that we want to do it via two interesting technologies: webauthn which quickly allows people to use public-key cryptography to access apps, and zero-knowledge proofs, ensure the apps do not have any knowledge of the delegated access apps gives to third-party issuers. In this last example we can think of how Ticketmaster has data on all their artists events, instead of the artists being the sole owners of their audience reach and data.

Funding requested (DAI/USDC): [$15,000.00]
For this initial round I would like to get started with 15k for the PoC stage of the project.


  • Milestone #1: Architecture and Overview - [$3,000]
    • Architectural Diagram
    • Software Requirements
    • App Specification
  • Milestone #2: PoC- [$12,000]
    • Desktop-compatible web app connected to either 3IDConnect w/a local P256 key or PassKey w/Webauthn
    • Composite for both public keys and general members lists for apps
    • API able to grant access based on DID connectivity for read/write on admin side
    • Generation of zero-knowledge proofs based on existance of a P256 key within an app Composite “realm”
    • Verification of the proof by a third party by querying CompositeDB against a specific app “realm”

We have two follow up requests upon completing the first milestone, but we want to asses the adoption and feasibility before requesting further grants. In short, we rather want to get a PoC out ASAP (perhaps something to showcase for ETH Denver) before asking a more structured grant as next steps. The last grant would be the Alpha (shown below), which upon completing we would like to look for investment outside the Ceramic ecosystem.

I accept the 3Box Labs Grants terms and conditions: [Yes]
I understand that I will be required to provide additional KYC information to the 3Box Labs to receive this grant: [Yes]


Hi @jjperezaguinaga, thank you for your proposal! We will be in touch with an update once we have completed our initial review (1-2 weeks).

Congratulations @jjperezaguinaga , I’m delighted to inform you that your grant proposal has been accepted! :tada:

We would like to award you a Ceramic Builders Grant.

We will follow up shortly with more details via email.

1 Like

@0x_Sam Thanks for the update! Should we do our updates in this thread, or does it make sense to create a new one and take it from there.

From now on, all updates and tasks will be handled in GitHub - 0xjjpa/zkAccess: An auth and access layer on top of the Ceramic Network using zkECDSA and webauthn

1 Like

Hi @jjperezaguinaga, at the risk of duplicating some work from GitHub, it would be great if you also shared updates here in this thread. This will make it easier for all Ceramic community members to follow your & others grant progress. Thanks for your understanding!


1 Like

[ Update 01/07 - v0.0.1 - zkAccess + Webauthn Demo ]

Here’s my first update for zkAccess grant.

Source code:



From the original proposal for zkAccess, its architecture looks as follows

The critical parts are the first three components:

  1. The Ethereum key provider used to interact with Ceramic from a user perspective.
  2. The Passkey generated on the client side to be able to create zkAttest proofs
  3. The CompositeDB schema and model to register the public keys.

The part that I had the least confidence in making it work was 2, as I wasn’t sure whether a P-256 webauthn key could be used to create a zkAttest proofs. This was mainly due to the format used by webauthn keys (ASN.1 over a “raw” format). Thus, my first update was focused on making this happen, and I focused the last two weeks doing so.

The first days were a bit frustrating, as I was trying to recreate the webauthn workflow entirely with a Relay Party (RP). Sadly, the entire RPC with an RP involved is quite extensive, and there aren’t many server’s or code out there showcasing this. After a few days, I capitulated trying to make the server obtain the webauthn key and decided to take a few baby steps first.

After reading again the webauthn specs, I realized I could mimic the entire code client side. In other words, I could “fake” the RP signing data as I did not want to create access over via a server but instead via a zero-knowledge proof (i.e. zkAttest). Thus, I proceeded to replicate the workflow client side, and after a few days, I managed to do so. Hooray!

Once I “mastered” the passkey client side, the last part was to interact with zkAttest. This was a bit trickier since, of course, zkAttest was coded with crypto.subtle.generateKey in mind, and I knew the format would be different. Luckily someone already solved this in a different context, which helped me then create a successful zkAttest proof against a bunch of P-256 public keys. We made it!

I ended up putting a UI on top of it to showcase this. Here’s how it looks in two devices (iOS and Android). The Android device is a significantly old version, hence the zkAttest proof generation taking almost 30 seconds on its own.

iOS Demo

Android Demo

Next steps

With 2 out of the way, we are now ready to get back to the actual Ceramic part of the application. I’m still not 100% of the current way we are handling user interaction with Ceramic (see my comment on the Discord’s group), so I’ll jump right into the CompositeDB. A successful integration here would look as follows:

  • We create a specific schema able to store a Public Key using email as an ID
  • We can create a composite able to query a given Public Key given an email
  • We can then store multiple Public Keys in our demo (instead of mocked ones) and proceed to replicate the same valid proof mechanism with real Public Keys from other users, making the demo closer to the end product.



1 Like

[ Update 01/27 - v0.0.2 - zkAccess + CompositeDB ]

Here’s the second update for zkAccess grant.



As described in the past update, the second step was to store the users’ public keys in our ComposeDB. It wasn’t evident to me the difference between traditional “streams” and ComposeDB until I managed to take a deeper dive into the technology. After I managed to implement did-session into the demo, I then proceeded to interact with my local Ceramic node trying to understand their roles. I created a thread in Discord to discuss these.

In the meantime, I started to put together the idea of the first “implementation” of zkAccess:

In short, the goal would be to have a website where I’ll allow users to manage a DID via their Ethereum account and generate a webauthn credential to store it in ComposeDB. Then, they can request to create a zk-proof against a specific NFT collection (e.g. BAYC). A server would filter the public keys (mapped against an address) for only the owners of this NFT collection, and then the owner could create the proof.

Next steps

I’ve managed to verify 1 and 3 from the original post (i.e., write in Ceramic from an Ethereum account a zero-knowledge attestation data, and query these from ComposeDB). However, I struggled to integrate everything within the existing app. Thus, as a next step I’ll create the “actual” app using Create Ceramic App and import my zk libraries there. I’m hoping to have a fully functional demo by next update.


  • Time. I struggled to wrap my head around the new libraries and concepts, so I ended up putting together this thread for future developers - FAQ for common errors in ComposeDB + DIDs (Jan 23). I’ll have to speed up the demo since I’ll start from scratch with the Create Ceramic App template.
  • Production Deployment - I’ve yet to see how I’ll deploy to production ComposeDB, so that’s another concern (I mean, I can always ssh to a VM and just run the commands, but yeah, something more automatic would be great).

[ Update 02/19 - v0.0.3 - zkAccess + CompositeDB + Deployments]

Here’s the third update for zkAccess grant (a bit later, was meant for the 02/17 but we had to verify a few things).


  • Completed the schema for zkAccess after multiple tries while understanding what can, and can not be done in CompositeDB
  • Tried multiple times to deploy a Ceramic node, unsuccessfully. Ended up deciding to focus on the rest of the application instead
  • Gearing towards an ETH Denver release, although I’m still missing a Ceramic node and a few features for the app to fully work.


We are gearing towards the end of the application and hence the grant on my side. I’ve managed to complete the schema for our application, which ended up being a bit simpler after a few iterations. In short, for this particular demo, here’s the schema:

type Account @createModel(accountRelation: SINGLE, description: "The owner of a public key") {
  owner: DID! @documentAccount
  rawId: String! @string(maxLength: 64)
  publicKey: String! @string(maxLength: 200)

type Keyring @createModel(accountRelation: LIST, description: "A list of public keys") {
  owner: DID! @documentAccount
  title: String! @string(minLength: 10, maxLength: 100)
  keys: [String!] @list(maxLength: 1000) @string(maxLength: 200)

There were a few hiccups that slowed us down here. The first one was the lack of knowledge around current’s ComposeDB inability to filter (see How to query stream data? I mean is it possible to filter streams based on the data inside the stream? and Queries by fields - #8 by Sami). This meant that I had to add a DID @documentAccount directive to all my models as it would otherwise be unable to fetch the current user keyring and current credential. It’s also not so obvious how to do this, until you find this part of the documentation - Queries | ComposeDB on Ceramic.

The second drawback was not having an “append” ability into arrays, but a simply replace server mutation (e.g. update$Model). This meant that if you had any dynamic-like properties in your model, then you would need to retrieve all existing values for that particular array-like property, and then use the update mutation. Not the worst, but also not the best.

The last drawback was not related to ComposeDB, but more to webauthn. In short, we wanted to use the public key created during navigator.credentials.create later on via navigator.credentials.get, but seems like the current relay model expects the server to “store” these public keys. In our current model, this would mean our ComposeDB, which we unlinked in last update to avoid anonymity. Now we need to store the publicKey, and link it to current DID, which shows up the user’s address. Not the worst, but now it’s obvious which club contains which people. Verification is still done via zero-knowledge proofs, but that linkage is now evident to viewers of the network.

User Interface

We completed our final UI, which had a bit of a refactor to accommodate ETH Denver.

For now, we are only allowing people to create a single club (“ETH Denver”) to make easier later on aggregate multiple models. This can be done after ETH Denver, but the important part is to be able to gather keys. Our final goal is to achieve a similar experience ENS gave during DevCon Bogota (see with their Leaderboard (Swag - ENS Domains)

We also spent some time to make sure it works on a desktop. We are expecting most of the user experience to be mobile, but we want to make sure it also works on people’s desktops.

We also finished an edge-case where Chrome was requiring only Yubikeys, but finally manage to also read the public key the browser or device would provide.

Next Steps

We have now completed to verify all 3 steps from the original post (i.e, we can write from an Ethereum wallet webauthn data to Ceramic, we can query this in a string-like format from ComposeDB queries, and we can restore all this data to create zero-knowledge proofs). Since we were pushing a few bleeding-edge features, we stumbled upon more walls than expected.

The next steps are simply to finish the application, and getting it ready for ETH Denver. We are expecting to require some 4-5 hours more to have it “ready to use”. Deployments for both website and Ceramic node are pending.


  • Production Deployment (still) - I have to say that I wasted too much time to my comfort to get a Ceramic node up and running in a VPS environment. I created GitHub - 0xjjpa/zkaccess-infra: Setting up a Ceramic prod node via Terraform / Ansible using GitHub Actions which relied in the ceramic-poc-infra template, and was unsuccessful to achieve a stable node. Furthermore, the Terraform script did not include a proper storage backup, so if it failed once, you would have to delete the components in your datacenter manually. You can see other efforts I did in Discord. Also, I guess there are tons of main net changes and network changes atm, so sometimes my node goes kaput without giving much chance of recovery. It would be a bit of a pity to have the app ready, and just not being able to use because the Ceramic node or network would be fault. Oh well :slight_smile:

Since the last update will be anyway next week before ETH Denver, any updates here will be done here in this post directly.

1 Like

[ Update 02/26 - v0.1.0 - zkAccess LIVE :tada: ]

Here’s the fourth update and final update for zkAccess grant (for now, expecting to push a few things on my own, particularly documentation wise).


  • We completed the entire write-read cycle in our ComposeDB for the zero-knowledge proofs. We had a major issue with the zkp-ecdsa library that we managed to solve after some hours of debugging.
  • Deployed both our ComposeDB Ceramic instance in a server after some struggles. Ended up being a bare-bones metal server I can monitor in case of downtimes, so there’s that! Hoping to use some provisioning later on.
  • Released the application with all its feature on There are a few outstanding issues but we’ll sort them out.


They say a demo is worth a thousand words!

There are three main actions a user can do:

  • Create a circle and an account, where they create their groups and themselves to it.
  • Register friends or be registered, by scanning or getting your zKey scanned.
  • Create proofs and verify them, which will show valid or false, but won’t show who from the club created the proof.

Here are the main workflows shown in multiple gifs.

Creating a circle and showing your ID

Loading your passkey and adding yourself to your circle

Generating an (invalid) proof for a circle you have scanned

This generates this from the side of the club owner

Generating a valid proof for a circle your are part of

This generates this from the side of the club owner

That’s it all on my side, all milestones had been completed! I hope you all enjoy it, and feedback is always welcome. I’ll polish some of the UI errors and expand the documentation, and look forward to Ceramic’s next steps. Enjoy ETH Denver!

Great work @jjperezaguinaga ! Really excited to see the demo and have everyone play around with it at ETH Denver.

For the next steps, the Grants Team will review your milestone submission and will come back with any feedback or clarifying questions. Following that, we will remit the final milestone payment. As a heads-up, the team is a bit backlogged with ETH
Denver + ComposeDB Beta release, so we will likely not be able to get to this until next week at the earliest.

Congratulations again!

Hi Sam!

Thanks for your kind words. Please use (and break ha) the app and send the feedback my way. I’ll continue to update it, as already got some interest on my side. Happy to hear from the milestones Grants Team :slight_smile:

Take your time with the final milestone payment, there’s no rush on my side! Please enjoy ETH Denver and keep releasing awesome stuff. Just don’t forget to have fun as well!

Looks great!
I’d love to learn more about your experience with WebAuth please, do you see it as ready for wider use-cases or mostly proofs of concepts for now?

Yes! I was actually surprised that it had already some typings. Documentation and examples in the web are scarce but not like they were a few years ago. It’s also clear some companies like Cloudflare are doing a major push on favor to it.

Passkeys were a major improvement over existing infrastructure, but documentation across all supported schemes is still lacking. For instance, I toyed with a Yubikey C (see attached) for the demo and it failed because I mistakenly called my key ID length to 64 bytes, which is not documented anywhere… but a normal FIDO2 simulated key fits in there!

Anyway, from a blockchain perspective, I expect with Account Abstraction an L2s making gas limits irrelevant, we’ll see more use cases of webauthn workflows (eg P256 signatures) for wallets and other processes. I believe there are already some demos out there shown in ETH Denver (Banana Wallet and Raise Finance) that are already using wallets with webauthn workflows.

1 Like