Getting Started
The Zapp SDK lets you build JavaScript apps which connect to Zupass, giving you access to programmable cryptography tools which enable secure and private interaction with the user’s personal data.
In this guide we’ll look at how to get started as an app developer, connect to Zupass, and interact with the user’s data store.
Installation
To get started with the Zapp SDK, you will need to install the @parcnet-js/app-connector
package, using your preferred package manager:
Import the connector
Next, import the connector package in your application code:
Connect to Zupass
To connect, you will need to define some data about your Zapp, select a host HTML element, and choose a Zupass URL to connect to.
Defining your Zapp
To define your Zapp:
Try to give your Zapp a simple, memorable name that is clearly tied to its branding or domain name.
Permissions
When connecting for the first time, your Zapp will request permissions from Zupass. These permissions determine what the Zapp will be allowed to do with user data:
Permission | Effects | Parameters |
---|---|---|
READ_PUBLIC_IDENTIFIERS | This permission allows your Zapp to read the user’s public key and Semaphore commitments. | None. |
REQUEST_PROOF | This permission allows your Zapp to request zero-knowledge proofs from the user. | collections: The names of the collections that your Zapp requires access to. |
SIGN_POD | This permission allows your Zapp to request the signing of a POD with the user’s identity. | None. |
READ_POD | This permission allows your Zapp to read any POD from the specified collections. | collections: The names of the collections that your Zapp requires access to. |
INSERT_POD | This permission allows your Zapp to insert PODs to the specified collections. | collections: The names of the collections that your Zapp requires access to. |
DELETE_POD | This permission allows your Zapp to delete any POD from the specified collections. | collections: The names of the collections that your Zapp requires access to. |
“Collections” are distinct groups of PODs, and allow the user to grant different levels of access to different Zapps.
Select an HTML element
The app connector works by inserting an <iframe>
element into the web page, and uses this to exchange messages with Zupass. To avoid interfering with other elements on your page, you should add an element that the app connector can use. For example, your HTML might look something like this:
Here we’re using an element with the ID app-connector
to host the iframe
. It’s important that your application does not change this element once the app connector has started.
Choose a Zupass URL
Finally, you must choose the Zupass URL you want to connect to. The client is identified by the URL it’s hosted on. For Zupass production, the URL is https://zupass.org
, but you might be running a local development version on something like http://localhost:3000
. Or you might be running another client altogether, in which case use the URL that it’s hosted at.
Putting it all together
Now we’re ready to connect:
The resulting z
variable will contain an API wrapper. The API reference for this is here.
Creating and reading data
User data is stored in POD format. This is a cryptography-friendly data format which enables the creation of proofs about the structure, content, and provenance of individual data objects, or groups of data objects.
Signing PODs
New PODs are created by signing a data object. This is straightforward:
The result of the sign
method is a POD
data object. Internally, this stores the data entries as a Merkle tree, but it’s not necessary to understand how this works.
The POD’s unique signature is the signed hash of the Merkle root, but you can treat it as an opaque identifier. The signature is created using a private key belonging to the user, which is managed by Zupass - your app does not have direct access to the private key.
Inserting PODs to the data store
Once you have a POD, you can insert it to the data store:
Zupass is responsible for saving the data and synchronizing it between devices, so your app doesn’t need to worry about how this data is persisted.
The collection name should be one of the collections that your Zapp requested permission to insert PODs to. Attempting to insert to a collection without permissions will fail.
In the above example, we’re inserting the POD that we created by requesting a signature from the client, but your app could also have a back-end service which signs PODs using your own private key. Those PODs can be inserted using the insert
API. For example:
The details of your backend may be different, but this approach works for PODs produced by any external source.
Deleting PODs
Your app can also delete PODs for which it knows the signature:
This tells the client to drop the POD from the user’s data store.
Querying for PODs
To read PODs from the data store, you can define queries which describe certain criteria for PODs:
This will return any PODs which have entries matching the description. The result is an array of POD
objects, which will be empty if no matching results are found.
Queries can contain more advanced constraints:
This query provides a list of valid strings for the greeting
entry, and a range of valid numbers for the magic_number
entry. You can use these features to construct very specific queries for specific PODs.
Subscribing to queries
TODO
Making and verifying proofs about data
PODs are designed to make it easy to create cryptographic proofs about their structure, content, and provenance. These proofs are created using General Purpose Circuits, or GPCs.
GPCs allow you to describe aspects of a POD, and make proofs about that POD without revealing all of the underlying data. For example, you could prove that you have a POD signed by a particular public key without revealing the POD’s content, or prove that you have a POD with a particular entry whose value lies in a certain range or belongs to a list, without revealing the exact value.
Making proofs
To create a proof, you must create a proof request. The proof request contains information about what you would like to prove, and the user can decide whether to allow the proof. Here’s an example:
This proof will require a POD with a valid greeting
and magic_number
, and will also reveal the value of the email_address
entry.
The result has a field success
which is set to true
if the proof was created successfully.
If success
is false then the result also contains an error
field containing an error message.
If success
is true then the result has a further three fields:
proof
, which contains the cryptographic proof for later verificationboundConfig
, which contains the proof configurationrevealedClaims
, which contains the “claims” about the data which can be verified with reference to theproof
In the example above, the revealedClaims
field would contain something like this:
The membershipLists
contains information about the list membership rules that apply to some entries, and contains information that was part of the input. However, the pods
section contains details about the specific POD that the user selected, which was not part of your input.
Verifying proofs
TODO
User identity
Users have identity credentials, which their clients help them to manage. You can request certain public information about the user’s identity using the API.
Semaphore v3 commitment
The user’s Semaphore v3 commitment can be retrieved like this:
The commitment is a bigint
and uniquely identifies the user.
Semaphore v4 commitment
The user’s Semaphore v4 commitment can be retrieved like this:
The commitment is a bigint
and uniquely identifies the user.
Public key
The user’s Semaphore v4 public key can be retrieved like this:
The public key is a string
and uniquely identifies the user.