/zupass

Zuzalu Passport

Primary LanguageTypeScriptGNU General Public License v3.0GPL-3.0

Zupass

What is the Zuzalu Passport?

This README is generally aimed at developers interested in building on top of the Zuzalu Passport. If you are a resident looking for instructions on how to set up your Passport, visit this link.

The Zuzalu Passport allows Zuzalu residents to store personal data relating to Zuzalu identity, reputation, activity, and more, and to share any part of this data with any (physical and digital) Zuzalu service or app of their choosing. Zuzalu services may include anything built by Zuzalu administrators or other residents: physical authentication procedures, the Zuzalu website, Zuzalu message boards, Zuzalu governance infrastructure, a resident Mafia game, a community matchmaking service, a community newsletter, and more.

Every active Zuzalu resident and visitor has created a Zuzalu Passport, making it a simple but powerful "base" for anyone to build and experiment on top of. The goal of the Zuzalu Passport is to help the Zuzalu community collectively build out community infrastructure over the course of the next two months, by providing a solid foundation and by onboarding people onto new tools enabled by zero-knowledge.

Under the hood, the Zuzalu Passport stores cryptographically-manipulable data such as keypairs, credentials, attestations, and more, and uses a very lightweight standard for arbitrary zero-knowledge proofs to pass along this data to applications. ZKPs enable three critical features in the Passport system:

  • Any Zuzalu resident can build applications that use the Passport system, without needing permission from Zuzalu organizers or Passport maintainers. There are no API keys, centralized servers, proprietary standards, special tokens, or gated endpoints* that you need approval for in order to build an experimental community governance project that anyone with a Zuzalu Passport can participate in. With the Passport architecture, application developers can simply give or request data from users directly.
  • Zuzalu applications are interoperable by default, and can understand and "talk to" one another without the need for special permissioning. For example, one resident could build a message board where posters can accumulate "Zuzalu karma" for posting high-quality content, another resident can build a "private POAPs" service allowing Zuzalu event attendees to prove participation in community events, and a third resident could build a private polling app where users with either more "Zuzalu karma" OR provably high community event participation can cast votes carrying more voting weight--without the three application builders having to coordinate at all!
  • Users store and control their own data. Zuzalu user data is by default only accessible by the user on their own devices--not by the Zuzalu organizers, the Passport maintainers, or the developers of any Zuzalu applications. Additionally, thanks to ZKPs, users have total control over who they share this data with, and how.

As mentioned above, we hope for the Zuzalu Passport to enable more permissionless experimentation in community and governance infrastructure at Zuzalu. You can find a list of starter ideas for things to build on the Zuzalu Passport here. We'll also be running workshops and a hackathon track throughout ZK Week, for Zuzalu residents and visitors who are interested in hacking on top of the Passport!

*Currently, we run a server that serves a Merkle Tree of participant public keys and some metadata for convenience, though this could easily be migrated on-chain.

For Developers: Understanding the Zuzalu Passport Model

Zuzalu Passport Cards

The Zuzalu Passport holds a collection of cards. UX-wise, the Zuzalu Passport interface is similar in concept to the Apple Wallet.

Initially, the only card in the Zuzalu Passport wallet is a Semaphore keypair that acts as your primary identifier as a resident or visitor. This is a special card: it displays a QR code which you can use to prove that you are indeed a Zuzalu resident.

However, the Passport data model allows anyone to create new types of cards, and to publish a flow to allow others to add a card of this new type into their passport. The flow for adding a new type of card to your Passport will be similar to the Apple Wallet's "Add to Wallet" button. Examples of other cards you could build, and allow others to add to their Passports, include:

  • An Ethereum signature proving that you are janedoe.eth on Ethereum. (see the ETH PCD generator).
  • A signature from a Synthetic Biology subevent host, certifying that you attended the subevent, or that you're authorized to attend the subevent.
  • An email you've received from invites@zuzalu.org identifying the apartment number you're staying in.
  • An ETHdos-style recursive ZK proof, certifying that you are 2 degrees of connection away from Vitalik.
  • A ZKML proof composed with a timestamp server signature, certifying that you visited the Lustica Bay lighthouse on Sunday, April 2nd.

Any third-party service--for example, a Zuzalu voting app--can request a card, multiple cards, or some claim about one or multiple cards from the Zuzalu Passport (live examples). For those who have used Ethereum apps before, this is a similar flow to how a dapp website might ask you to sign a message or a transaction by popping up Metamask. A few more concrete examples of card requests that a third-party app could make:

  • You have a valid Semaphore keypair card such that the public key is in the latest Zuzalu Residents Merkle root.
  • You have either attended a subevent (i.e. have a signature from a subevent host certifying that you attended a subevent), OR you're within 2 degrees of connection away from Vitalik.
  • According to a public "Zuzalu Citizen Score" formula that takes into account subevents you've participated in, posts you've made on the Zuzalu anonymous messaging forum, and your contributions to open-source Zuzalu infrastructure on Github, you have a voting weight of at least 500 in the next Zuzalu election.
  • You participated in the Zuzalu ZKML scavenger hunt, and took pictures of all eight of the Zuzalu Artifacts in the last week.

Cards and Card Requests as "PCDs"

As a developer, if you are interested in working with the Zuzalu Passport and with Zuzalu Passport Cards, you'll need to understand the "PCD" abstraction.

"PCD" is short for "Proof-Carrying Data"*. In this repository, we use this term to refer to any claim about the world that is attached to a proof of its own correctness, without the need for external context to verify--such as a user card, or a response to a third-party request for information about user cards.

All PCDs consist of a "claim", which is the human-interpretable statement that the PCD is making (i.e. "I am a Zuzalu resident"); and a "proof" attached to the "claim," which is a cryptographic or mathematical proof of the claim. All PCDs within this SDK also expose a prove and verify function, which allow you to instantiate them, and verify that they are indeed correct.

Many PCDs are zkSNARKs. However, not all PCDs are zkSNARKs, or even zero-knowledge proofs. For example, one PCD that is not a zkSNARK is a piece of data signed by an RSA private key along with the corresponding public key:

{
  "publicKey": "12345...",
  "message": "Hello World!",
  "signature": "asdfg..."
}

This is a PCD because anyone can verify that what it claims is true by running the RSA signature verification locally.

*Our "PCD" abstraction is partially inspired by a spiritually similar academic cryptography concept of the same name. Note that the academic term has a slightly different technical definition.

What is the PCD SDK?

The PCD SDK is a framework for developing applications that use PCDs for the proper functioning of their core feature set. It defines the set of interfaces necessary for correctly reasoning about and processing PCDs. It defines the interfaces through which PCDs are produced and consumed. It also includes a "passport" web application that lets a user manage their personal PCDs, and enables third party applications to request PCDs from the passport, and add new PCDs into it.

For Developers: Local Development

Environment Variables

In order to develop locally, you will need to set some environment variables. The only place where this needs to happen is in the passport-server project. We have included an example environment variable file which allows you to start a minimal version of the passport application that depends on no external services. You can find this file here: apps/passport-server/.env.local.example. In order to make the passport-server use these environment variables, you will need to copy the contents of the example file into an adjacent file called .env.

If you are on the PCD team and need more environment variables, contact @ichub.

Running the project

In the root of this project, execute the following to start the servers and static sites locally.

# installs dependencies for all apps and packages in this repository
yarn

# starts local Postgres - you must have Postgres installed for this
# to work properly. in case you want to restart a Postgres instance
# you previously started in this project, you can also run the command
# yarn localdb:restart
yarn localdb:init && yarn localdb:up

# starts all the applications contained in the `/apps` directory of the
# repository. this includes the passport server and client, as well as
# a server and client for an example application built on top of the
# PCD sdk which integrates with the passport.
yarn dev

# open up the passport app in your browser.
open http://localhost:3000

Apps

The passport has its own client and server. We also included an example application set up with its own client and server as well (called the "consumer", whose apps are consumer-client and consumer-server). After running yarn dev all four "applications" will be available at the following ports:

  • apps/passport-client: http://localhost:3000/ - this is the application that allows users to manage their PCDs, and 3rd party applications to save or load PCDs from
  • apps/consumer-client: http://localhost:3001/ - this is an example 3rd party application, whose code demonstrates the API by which other 3rd party applications might interface with the passport, and how they might use the PCDs they get from the passport
  • apps/passport-server: http://localhost:3002/ - this is the server-side application which backs the passport client. currently it is used to manage Zuzalu participants, send confirmation emails, end-to-end encrypted backup of PCDs from the passport client, and serving some heavy assets for the client.
  • apps/consumer-server: http://localhost:3003/ - this is an example server application which would back an example 3rd party client app. currently this app is not useful as an example since it contains no meaningful example code, but we have included it for completeness, and for future examples.

Packages

This repository includes many packages. They are all published on NPM!

Some of these packages are used to share development configuration between the different apps. Others of these packages are intended to provide shared code that can be used on both a client and server. The third category of packages is PCD packages - those that implement the "Proof Carrying Data" interface. This final category of packages allows you to instantiate (prove), verify, serialize and deserialize various PCDs.

pcd packages

  • @pcd/semaphore-group-pcd: a pcd which wraps the Semaphore protocol, which allows PCD-consuming applications to consume and generate Semaphore proofs.
  • @pcd/semaphore-identity-pcd: a 'self-evident' PCD, representing the public and private components of a Semaphore identity
  • @pcd/semaphore-signature-pcd: like @pcd/semaphore-group-pcd, but with a more specific purpose of using the semaphore protocol to 'sign' a particular string message on behalf of a particular revealed commitment id.
  • @pcd/webauthn-pcd: a pcd that can be used to make claims about WebAuthn attestations, i.e. signing a particular challenge with a fingerprint, Yubikey, or another authorization gesture.
  • @pcd/ethereum-ownership-pcd: a pcd that can be used to claim that a Semaphore identity knows the private key of an ethereum address.
  • @pcd/ethereum-group-pcd: a pcd that can be used to claim that a Semaphore identity knows the private key of an ethereum address or public key that is part of a merkle group of addresses or public keys, without revealing the specific one.
  • ... more to come!

shared code packages

  • @pcd/passport-crypto: package that implements cryptographic primitives like encryption and hashing, to be used by the passport server and client.
  • @pcd/passport-interface: package that contains interfaces (both types and functions) that facilitate communication between the various components involved in a PCD application.
  • @pcd/pcd-types: package that defines what a PCD is.

utility packages

Testing

Each package and app which needs testing has a test script in its package.json. You can invoke all the tests serially by running the command yarn test at the root of the repository. Please make sure to write tests for anything you need to not break as we develop.

Linting

All the packages and apps are linted using eslint. The linting runs in GitHub Actions, and your branch must pass all the linting rules before it is merged. To run the linter locally, you can run the command yarn lint in the root of this project to lint all the packages and apps in the repository. If you want to run the linter on a particular project or package, navigate to its directory, and execute yarn lint from there.

For Developers: Zupass Production Deployments

Zupass

PCDpass

Example PCD App

For Developers: Adding a new PCD Type

PCDPackage

Zupass can support many types of PCDs. In order to create a new type of PCD that can be interpreted, verified, created, shared, and stored by Zupass, the first thing you must create is a new PCDPackage - a concrete implementation of the PCDPackage typescript interface as defined here:

https://github.com/proofcarryingdata/zupass/blob/main/packages/pcd-types/src/pcd.ts#L34-L49

Two example implementations of a PCDPackage that works with Zupass are:

Integrating with Zupass

In order for a new type of PCD to work with Zupass, its implementation must be added as a dependency of the apps passport-server and passport-client in the Zupass repository. This can be done by editing their respective package.jsons.

Next, the PCD implementation (as represented by its PCDPackage) must be added to a few places in the Zupass codebase:

Adding the new PCDPackage to the appropriate places is necessary for Zupass to be able to 'handle' the new type of PCD correctly.

Here are a few example pull requests that integrate a new PCDPackage into Zupass are:

The Zupass team reserves the right to reject any proposed PCD according to our discretion.

Internal vs. External

Some PCDPackage implementations live inside of the Zupass repository:

Others live outside off the Zupass repository:

The choice between Internal and External is yours to make. In either case, we will review the code for security vulnerabilities, testing, code quality, and documentation.

Testing

We recommend that you add an example for how a developer may create and consume your new type of PCD in the consumer-client app included in this repository. Check out how other PCDs have done this by navigating to http://localhost:3001/ after running yarn dev in the root of your project - this is where the consumer-client application lives.

We also recommend that you create a comprehensive test suite for your new PCD, so that we can be confident in your implementation. A few test suites we think are good can be found in the following PCD implementations: