# Constraining Data Access

In this tutorial, we will see how to constrain data access robustly with the Parcel SDK.

# Grants Specification Language

Parcel supports a domain-specific language for specifying access conditions under which data can be accessed when creating grants. You can see the language specification here.

Let's explore how we can use this within the Parcel SDK by extending Acme's compute job from the previous tutorial.

# Constraining Access to Jobs

Previously, Acme was able to run a compute job over Bob's data and generate an output document with the results. However, Bob created a grant which gave Acme direct access to his input document, and Acme was able to download (and view) it.

const recipeDownload = parcelAcme.downloadDocument(recipeDocument.id);
const recipeSaver = fs.createWriteStream(`./bob_data_by_acme`);
try {
  console.log(`Attempting to access Bob's document...`);
  await recipeDownload.pipeTo(recipeSaver);
  console.log('Successful download! (this was expected)');
} catch (error: any) {
  console.log(`Acme was not able to directly access Bob's data: ${error}`);
}

Let's now extend this grant to prevent Acme from directly downloading and viewing Bob's data, and only being given permission to execute compute jobs that use the bash docker image. This can be done as follows:

await parcelBob.createGrant({
  grantee: acmeId,
  condition: {
    $and: [{ 'document.id': { $eq: recipeDocument.id } }, { 'job.spec.image': { $eq: 'bash' } }],
  },
});

This grant now has two clauses that must both be satisfied to satisfy the entire grant condition, as indicated by the $and operator:

  • 'document.id': { $eq: recipeDocument.id } means that the document Acme can access must be the uploaded recipe document
  • 'job.spec.image': { $eq: 'bash' } means that Acme can only access permitted documents (in this case, the recipe document) by submitting a job with image set to bash.

After this grant is applied, Acme is no longer able to directly access Bob's data, and trying to do so should hit the failure case instead:

const recipeDownload = parcelAcme.downloadDocument(recipeDocument.id);
const recipeSaver = fs.createWriteStream(`./bob_data_by_acme`);
try {
  console.log(`Attempting to access Bob's document without permission...`);
  await recipeDownload.pipeTo(recipeSaver);
} catch (error: any) {
  console.log(`Acme was not able to directly access Bob's data (this was expected): ${error}`);
}

However, the rest of the job can proceed as normal so long as the job spec specifies the bash image to be used, and we can still read the outputs as Bob once the job completes:

console.log('Downloading output document as Bob.');
const outputDownload = parcelBob.downloadDocument(job.status.outputDocuments[0].id);
const outputSaver = fs.createWriteStream(`/tmp/output_document`);
await outputDownload.pipeTo(outputSaver);
const output = fs.readFileSync('/tmp/output_document', 'utf-8');
console.log(`Here's the computed result: "${output}"`);

Under the hood, when a Parcel worker receives a job submission, it attests the program it wishes to run (in the form of the Docker image) to Parcel, along with additional contextual information about the job execution context. Parcel then validates that this context is allowed by the grant condition before releasing any input document(s) encryption key(s) to the worker.

# Specifying Allowed Job Images

In this example, we specified the allowed image context as bash for simplicity, which defaults to bash@latest. As the latest bash docker image evolves and is updated, so will the image Acme is allowed to execute over Bob's data.

If Bob wants to pin the allowed docker image to a specific version, he can do so by including the docker digest (opens new window) for the desired image in the grant condition:

'job.spec.image': {
  $eq: 'bash@sha256:137d3e8797d78ab61c3bc4d599ff34dc7fcfbfca4a031c49ca83efed6792ab15',
}

# Next Steps

In this tutorial, we saw how to extend grants beyond unrestricted document access into constrained access within specific access contexts. Acme was able to generate insight for Bob from Bob's data without ever having direct access to the provided input or output document.

However, counting words in a file is a rather simple and contrived example. In the next tutorial, we'll take another step forward and show you how to design custom docker images to unlock the full power of Parcel compute jobs in complex real-world applications.

EXAMPLE

You can view the full example in the Parcel Examples repository.