Part 1: Introduction to parties
Learn the fundamental concept of parties in NPL and how they enable entities to participate in protocol relationships.
Prerequisites
- Be familiar with basic NPL concepts such as protocols and permissions
- Have completed the Modeling a simple use-case in NPL guide
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:
aliceandbobare distinct parties- The system can tell them apart:
alice != bob - But
alice == alicealways 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,approverinprotocol[editor, approver]) - Bound party: The party value specified during protocol instantiation (e.g., when the value of party variable
aliceis bound to theeditorparameter) - 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:
alicerepresents an individual (who is given the role of editor on theDocumentinstance)bobrepresents another individual (who is given the role of approver on theDocumentinstance)- 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.