Skip to content

Part 1: Introduction to parties

Learn the fundamental concept of parties in NPL and how they enable entities to participate in protocol relationships.

Prerequisites

Learning outcomes

After completing this part, you'll be able to:

  • Understand what parties represent in NPL
  • Differentiate between different parties in your protocols
  • Create unique party values for individuals and groups
  • Recognize when two party references represent the same entity

What are parties?

In NPL, parties represent entities that can act in protocol relationships. Think of parties as the actors in your business processes—they are the "who" that can perform actions, hold permissions, and participate in multi-party workflows.

Entities acting in relationships

At its core, a party is an abstract representation of an entity that participates in a protocol. This entity could be:

  • An individual (e.g., Alice, Bob, a specific user)
  • A group or organization (e.g., the Finance Department, the Legal Team, a company)
  • Any other distinguishable actor in your business logic

The key insight is that parties enable you to model who can do what in your protocols, without initially worrying about the complexities of user information management, authentication, or organizational structures.

Creating parties: The uniquePartyOf() function

When you're writing tests in NPL, the language provides a simple way to create unique party values using the uniquePartyOf() function. This function generates a new, unique party each time it's called.

Beyond the uniquePartyOf() function, there are several ways to instantiate parties, and the most important one will be discussed in part 3.

Benefits of uniquePartyOf()

  • Simple: No need to manage complex data structures or IAM systems initially
  • Distinguishable: You can differentiate between Bob, Alice, and the Finance Department
  • Comparable: The system can recognize that bob == bob, which is essential for permission checks

Example: Creating parties for individuals

// Create unique parties for individuals
var alice = uniquePartyOf();
var bob = uniquePartyOf();

// Create a document with these parties
var document = Document[alice, bob]("Quarterly Report");

In this example:

  • alice and bob are distinct parties
  • The system can tell them apart: alice != bob
  • But alice == alice always holds true, enabling permission checks

Example: Creating parties for groups

Parties aren't limited to individuals—they can represent any entity:

// Create unique parties for organizational units
var financeTeam = uniquePartyOf();
var legalTeam = uniquePartyOf();

// Create a contract review protocol
var review = ContractReview[financeTeam, legalTeam]("Annual Contract");

Here, financeTeam and legalTeam are abstract groups. At this level, we don't need to know:

  • Who is in the Finance Team
  • How members are added or removed
  • What authentication system verifies membership

We just need to know that the Finance Team and Legal Team are distinct entities that can act in the protocol.

Note: We may have use cases where alice and/or bob as individuals belong to the finance team or the legal team and should be authorized as such. In that case, more general Party constructs (using claims, see Part 3: Party assignment) should be used.

Understanding party reusability

Parties can be reused

Once created, a party can be used across multiple protocol instances:

var alice = uniquePartyOf();
var bob = uniquePartyOf();

// Alice and Bob collaborate on multiple documents
var document1 = Document[alice, bob]("Q1 Report");
var document2 = Document[alice, bob]("Q2 Report");
var document3 = Document[bob, alice]("Q3 Report");

The same alice party appears in all three documents. The statement var alice = uniquePartyOf() should be understood as the binding of a party variable value, created with the uniquePartyOf() function, to a party variable identifier alice, allowing reusability.

Understanding party terminology

Before we continue, let's clarify some key terminology:

  • Party value: A value of the Party type (created e.g. by uniquePartyOf()) that can be bound to a party variable identifier or a protocol party parameter (see below)
  • Party identifier: A party variable name (e.g. alice, bob)
  • Party parameter: A party variable name in the protocol header (e.g., editor, approver in protocol[editor, approver])
  • Bound party: The party value specified during protocol instantiation (e.g., when the value of party variable alice is bound to the editor parameter)
  • Calling party: The party initiating an action/permission (e.g., the party value in doc.edit[alice]())

These concepts work together: when you call Document[alice, bob], you bind the party values of party variables alice and bob to the party parameters editor and approver. Later, when you call doc.edit[alice](), alice is the calling party, and the system checks if its value at call time matches the bound party for the editor parameter.

For more details, see Party reference documentation.

Party equality enables permission checking

When a permission is invoked, NPL checks if the calling party matches the bound party:

protocol[editor, approver] Document(
    var content: Text
) {
    initial state created
    state inReview
    final state approved

    permission[editor] edit(newContent: Text) | created {
        content = newContent;
    }

    permission[editor] requestReview() | created {
        become inReview;
    }

    permission[approver] approve() | inReview {
        become approved;
    }
}

var alice = uniquePartyOf(); // some value is bound to alice
var bob = uniquePartyOf(); // another value is bound to bob
// This instantiates a Document protocol, binding the value of party variable alice to party parameter editor, and the value of bob to approver
var doc = Document[alice, bob]("Initial content");

// This works: alice is the calling party and its value matches the bound party for editor
doc.edit[alice]("Updated content");

// This would fail: bob is the calling party but its value doesn't match the bound party for editor
// doc.edit[bob]("Wrong party"); // ❌ Permission denied

In the permission call, the system checks if the value of calling party alice matches the bound party for the editor parameter. Since the value of alice was bound to editor and alice == alice, the permission check succeeds.

Practical example: Document approval workflow

Let's see how parties work in the complete document approval workflow:

protocol[editor, approver] Document(
    var content: Text
) {
    initial state created
    state inReview
    final state approved

    permission[editor] edit(newContent: Text) | created {
        content = newContent;
    }

    permission[editor] requestReview() | created {
        become inReview;
    }

    permission[approver] approve() | inReview {
        become approved;
    }
}

// Create parties
var alice = uniquePartyOf();
var bob = uniquePartyOf();

// Create document with alice as editor, bob as approver
var document = Document[alice, bob]("Initial draft");

// Alice edits the document
document.edit[alice]("Revised content");

// Alice requests review
document.requestReview[alice]();

// Bob approves the document
document.approve[bob]();

In this example:

  • alice represents an individual (who is given the role of editor on the Document instance)
  • bob represents another individual (who is given the role of approver on the Document instance)
  • Each party has distinct permissions at different stages of the workflow
  • The system enforces that only the correct party can perform each action

Note: In this example, party bob is defined as approver from the outset, at protocol instantiation. More advanced modelling (out of scope for this how-to part) would allow to define party bob as approver at a later stage, e.g. at the review request stage.

Testing with parties

NPL's testing framework makes it easy to verify party-based logic:

@test
function testDocumentWorkflow(test: Test) -> {
    var alice = uniquePartyOf();
    var bob = uniquePartyOf();

    var document = Document[alice, bob]("Initial draft");

    // Test that alice can edit the document
    document.edit[alice]("Revised content");
    test.assertEquals(document.content, "Revised content");

    // Test that alice can request review
    document.requestReview[alice]();
    test.assertEquals(
        document.activeState().getOrFail(),
        Document.States.inReview
    );

    // Test that only bob (approver) can approve (not alice)
    test.expectFails(function() -> {
        document.approve[alice]();
    }, "Alice (editor) should not be able to approve");

    // Bob successfully approves
    document.approve[bob]();
    test.assertEquals(
        document.activeState().getOrFail(),
        Document.States.approved
    );
}

Key concepts recap

Concept Description Example
Party The NPL representation of an entity, used to define and enforce authorization on protocols -
Party data type The data type defined in NPL to create, store or process parties -
Party variable Variable of the Party type, created to hold a Party value var alice = uniquePartyOf();
Party value A specific Party-typed value representing an entity uniquePartyOf() in var alice = uniquePartyOf();
Party identifier A party variable name alice in var alice = uniquePartyOf();
Party parameter A party variable name in a protocol header editor in protocol[editor, approver]
Bound party The party value assigned to a party parameter during protocol instantiation The value of alice bound to editor in Document[alice, bob]
Calling party The party initiating an action/permission The value of alice in doc.edit[alice](...)
Individual party A party representing a single person A specific user or employee
Group party A party representing a collection or organization Finance Department, Legal Team
Party equality Two party references are equal if they represent the same entity (as encoded in the party value) alice == alice (true), alice == bob (false, if alice and bob hold two different values)
Party reusability The same party can participate in multiple protocols alice used in multiple document instances

What's next?

In this part, you learned the basics of parties as abstract entities that participate in protocols. You now understand:

  • How to create unique party values with uniquePartyOf()
  • The difference between party values, party identifiers (party variable names), party parameters, bound parties, and calling parties
  • How to use parties for both individuals and groups
  • How party equality enables permission checking

In the next part, you'll learn about party delegation, which allows protocols to orchestrate actions on behalf of multiple parties, enabling complex multi-party workflows.

Further reading