PolicyBuilder
The PolicyBuilder provides a fluent, chainable API for constructing policies.
PolicyBuilder simplifies the creation of PD-REL policies by providing a type-safe, intuitive interface with method chaining. It handles the complexity of policy structure while ensuring that required fields are properly set and validates the policy before returning it.
Common Usage Patterns
Examples
// 1. Basic Usage Rights
const basicPolicy = new PolicyBuilder('ipfs://QmPolicy123')
.setTarget('ipfs://QmAsset456')
.addRule(rule => rule
.permit('USE')
.withDescription('Allow general usage')
)
.build();
// 2. NFT with Royalties
const nftPolicy = new PolicyBuilder('ipfs://QmNFTPolicy')
.setTarget('caip19:eip155:1/erc721:0xNFT.../42')
.addRule(rule => rule
.permit('TRANSFER')
.withDuty({
type: 'ROYALTY',
value: { percentage: 5, recipient: 'creator.eth' }
})
)
.addRule(rule => rule
.permit('DISPLAY')
)
.build();
// 3. Time-Limited Access
const subscriptionPolicy = new PolicyBuilder('ar://SubPolicy')
.setTarget('https://content.example.com/premium/*')
.addRule(rule => rule
.permit('USE')
.withConstraint({
type: 'TIME_PERIOD',
value: {
start: Math.floor(Date.now() / 1000),
end: Math.floor(Date.now() / 1000) + 2592000 // 30 days
}
})
)
.build();
// 4. Multi-Tier Access with OR Logic
const tieredPolicy = new PolicyBuilder('ipfs://QmTiered')
.setTarget('ipfs://QmPremiumContent')
.setLogic('OR') // Any rule can grant permission
.addRule(rule => rule
.permit('USE')
.withDescription('Token holders get free access')
.withConstraint({
type: 'TOKEN_GATED',
value: { chainId: 1, contractAddress: '0xTOKEN...', minBalance: 1 }
})
)
.addRule(rule => rule
.permit('USE')
.withDescription('Or pay per view')
.withDuty({
type: 'PAYMENT',
value: { amount: '5.00', currency: 'USDC', recipient: 'treasury' }
})
)
.build();
// 5. Creative Commons Style Policy
const ccPolicy = new PolicyBuilder('ipfs://QmCCStyle')
.setLegalContract('https://creativecommons.org/licenses/by-nc/4.0/')
.addRule(rule => rule
.permit('USE')
.withDuty({
type: 'ATTRIBUTION',
value: 'Original work by Artist Name'
})
)
.addRule(rule => rule
.permit('MODIFY')
.withDuty({
type: 'ATTRIBUTION',
value: 'Based on work by Artist Name'
})
)
.addRule(rule => rule
.prohibit('COMMERCIALIZE')
.withDescription('Non-commercial use only')
)
.build();
// Policy with pre-built rules
const prebuiltRule: Rule = {
action: 'REPRODUCE',
effect: 'permit',
constraints: [{ type: 'USAGE_COUNT', value: 100 }]
};
const mixedPolicy = new PolicyBuilder('ipfs://QmMixed')
.setTarget('https://api.example.com/assets/789')
.addRule(prebuiltRule) // Add pre-built rule
.addRule(rule => rule // Add rule using builder
.permit('DISPLAY')
.withDuty({ type: 'ATTRIBUTION', value: 'Art by Creator' })
)
.build();
// Error handling
try {
new PolicyBuilder('ipfs://QmEmpty').build(); // No rules!
} catch (error) {
console.error(error.message); // "A policy must have at least one rule."
}
Definition
class PolicyBuilder {
constructor(policyURI: string);
setTarget(target: string): this;
setLegalContract(uri: string): this;
setLogic(logic: 'AND' | 'OR'): this;
addRule(rule: Rule | (ruleBuilder: RuleBuilder) => RuleBuilder): this;
build(): Policy;
}
Constructor
Creates a new PolicyBuilder instance.
Initializes a new policy with the required policyURI and profile. The policy starts with an empty rules array that must be populated before calling build().
Parameters:
policyURI(string) - A unique, persistent URI identifying this specific policy. Should ideally be a content-addressed URI (IPFS, Arweave).
Examples
// Using IPFS hash
const builder = new PolicyBuilder('ipfs://QmXxx123...');
// Using Arweave transaction ID
const builder = new PolicyBuilder('ar://abc123...');
// Using HTTPS URL (less decentralized)
const builder = new PolicyBuilder('https://policies.example.com/v1');
Methods
setTarget
Sets the default target asset for the entire policy.
The target identifies which asset(s) this policy applies to. Individual rules can override this with their own target field. If not set, each rule must specify its own target.
setTarget(target: string): this
Parameters:
target(string) - The asset identifier (e.g., URI, CAIP-19)
Returns: this
This PolicyBuilder instance for method chaining
Examples
// IPFS content
builder.setTarget('ipfs://QmAsset123...');
// NFT using CAIP-19 format
builder.setTarget('caip19:eip155:1/erc721:0xBC4CA0EdA.../1234');
// Arweave data
builder.setTarget('ar://DataTransactionId');
// HTTP resource
builder.setTarget('https://api.example.com/assets/456');
setLegalContract
Sets the human-readable legal contract URI.
Links this machine-readable policy to its corresponding legal document. The legal contract is the authoritative source in case of disputes.
setLegalContract(uri: string): this
Parameters:
uri(string) - The URI pointing to the full, human-readable legal contract
Returns: this
This PolicyBuilder instance for method chaining
Examples
// Link to legal document on HTTPS
builder.setLegalContract('https://legal.example.com/contracts/nft-license-v2');
// Legal document stored on IPFS
builder.setLegalContract('ipfs://QmLegalDoc...');
// Legal document on Arweave
builder.setLegalContract('ar://LegalContractTxId');
setLogic
Sets the logic for evaluating the rules array.
Determines how multiple rules are combined when evaluating permissions.
- 'AND': All applicable rules must be satisfied (default)
- 'OR': At least one applicable rule must be satisfied
setLogic(logic: 'AND' | 'OR'): this
Parameters:
logic('AND' | 'OR') - The logical operator ('AND' or 'OR')
Returns: this
This PolicyBuilder instance for method chaining
Examples
// AND logic - all conditions must be met
builder.setLogic('AND')
.addRule(rule => rule.permit('USE').withConstraint(timeConstraint))
.addRule(rule => rule.permit('USE').withDuty(paymentDuty));
// Result: User must be within time period AND pay
// OR logic - any condition grants permission
builder.setLogic('OR')
.addRule(rule => rule.permit('TRANSFER').withConstraint(tokenGate))
.addRule(rule => rule.permit('TRANSFER').withDuty(payment));
// Result: User can transfer if they have token OR if they pay
addRule
Adds a new rule to the policy.
Rules can be added in two ways:
- As a pre-constructed Rule object
- Using a function that receives a RuleBuilder (recommended)
addRule(rule: Rule | (ruleBuilder: RuleBuilder) => RuleBuilder): this
Parameters:
rule(Rule | (ruleBuilder: RuleBuilder) => RuleBuilder) - Either a Rule object or a function that configures a RuleBuilder
Returns: this
This PolicyBuilder instance for method chaining
Examples
// Method 1: Using RuleBuilder function (recommended)
builder.addRule(rule => rule
.permit('USE')
.withDescription('Allow usage with attribution')
.withDuty({ type: 'ATTRIBUTION', value: 'Created by Artist' })
);
// Method 2: Pre-constructed Rule object
const customRule: Rule = {
action: 'REPRODUCE',
effect: 'permit',
constraints: [{ type: 'USAGE_COUNT', value: 100 }]
};
builder.addRule(customRule);
// Adding multiple rules
builder
.addRule(rule => rule.permit('USE'))
.addRule(rule => rule.permit('DISPLAY').withDuty(attributionDuty))
.addRule(rule => rule.prohibit('COMMERCIALIZE'));
// Complex rule with multiple constraints and duties
builder.addRule(rule => rule
.permit('MODIFY')
.withDescription('NFT holders can modify with conditions')
.withConstraint({ type: 'TOKEN_GATED', value: tokenGateConfig })
.withConstraint({ type: 'TIME_PERIOD', value: timeWindow })
.withDuty({ type: 'ATTRIBUTION', value: 'Modified from...' })
.withDuty({ type: 'SHARE_ALIKE', value: policyURI })
);
build
Finalizes and returns the constructed Policy object.
Validates that the policy has at least one rule before returning. The returned policy is ready for serialization, storage, or use with PolicyParser for querying.
build(): Policy
Returns: Policy
The completed Policy object
Throws:
- If the policy has no rules
Examples
// Successful build
const policy = new PolicyBuilder('ipfs://QmPolicy')
.setTarget('ipfs://QmAsset')
.addRule(rule => rule.permit('USE'))
.build();
// Store the policy
const policyJson = JSON.stringify(policy);
await ipfs.add(policyJson);
// Use with PolicyParser
const parser = new PolicyParser(policy);
const canUse = parser.isPermitted('USE');
// Error case - no rules
try {
new PolicyBuilder('ipfs://QmEmpty').build();
} catch (error) {
console.error(error.message); // "A policy must have at least one rule."
}
See Also
-
- Policy for the structure being built
- RuleBuilder for rule construction
- validatePolicy for validation logic
Since
Version 1.0.0