Skip to main content

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:

  1. As a pre-constructed Rule object
  2. 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

Since

Version 1.0.0