@grantex/dpdp
v0.1.0
Published
DPDP Act 2023 & EU AI Act compliance module for AI agents using the Grantex authorization protocol
Maintainers
Readme
@grantex/dpdp
DPDP Act 2023 & EU AI Act compliance module for AI agents using the Grantex authorization protocol.
Table of Contents
- What is @grantex/dpdp?
- Regulatory Coverage
- Installation
- Quick Start
- API Reference
- Type Definitions
- Security Considerations
- Related Packages
- License
What is @grantex/dpdp?
India's Digital Personal Data Protection Act, 2023 (DPDP Act) and the EU AI Act impose strict requirements on how personal data is collected, processed, and governed — especially when AI agents act on behalf of humans.
@grantex/dpdp bridges the Grantex delegated authorization protocol with these regulations. It
provides a purpose-linked consent management layer, grievance redressal, data principal rights
enforcement, and machine-readable compliance exports that satisfy both Indian and European
regulatory requirements.
Key capabilities
| Capability | Description | |---|---| | Consent Records | Immutable, Ed25519-signed consent records linked to Grantex grant tokens | | Purpose Enforcement | Runtime checks that grant scopes satisfy declared processing purposes | | Consent Withdrawal | Immediate withdrawal with optional grant revocation and data deletion | | Grievance Redressal | File and track grievances per DPDP Act Section 13 | | Data Principal Rights | Access, erasure, and portability APIs per Sections 11-13 | | DPDP Audit Export | Structured export for Data Protection Board of India submissions | | GDPR Article 15 Export | Machine-readable data subject access request response | | EU AI Act Conformance | Conformance report covering Articles 9-15, 26, and 50 | | Region Configuration | India (IN) and EU region-specific settings |
Regulatory Coverage
DPDP Act 2023 Mapping
| DPDP Section | Requirement | @grantex/dpdp Feature |
|---|---|---|
| Section 4 | Processing for lawful purpose | enforcePurpose() — runtime scope-to-purpose check |
| Section 5 | Notice to data principal | createConsentNotice() — versioned, hashed notices |
| Section 6 | Consent | createConsentRecord() — Ed25519-signed consent proof |
| Section 6(4) | Withdrawal of consent | withdrawConsent() — immediate, with grant revocation |
| Section 8(7) | Retention limitation | retentionUntil field on consent records |
| Section 11 | Right to information | getDataPrincipalRecords() — access API |
| Section 12 | Right to correction & erasure | requestDataErasure() — erasure request |
| Section 13 | Grievance redressal | fileGrievance() — 7-day resolution tracking |
| Section 17 | Record keeping | requestDpdpExport() — audit trail export |
EU AI Act Mapping
| EU AI Act Article | Requirement | @grantex/dpdp Feature | |---|---|---| | Article 9 | Risk management | Covered in conformance report | | Article 10 | Data governance | Purpose limitation + consent records | | Article 11 | Technical documentation | Export includes system documentation | | Article 12 | Record-keeping | Full audit trail with action logs | | Article 13 | Transparency | Consent notices with purpose descriptions | | Article 14 | Human oversight | Consent method tracking (explicit-click / api-delegated) | | Article 15 | Accuracy & security | Ed25519 signatures, SHA-256 notice hashes | | Article 26 | Deployer obligations | Compliance exports for deployers | | Article 50 | GPAI transparency | Conformance report for general-purpose AI |
GDPR Cross-Compliance
| GDPR Article | Requirement | @grantex/dpdp Feature |
|---|---|---|
| Article 6 | Lawful basis | legalBasis field on purposes |
| Article 7 | Conditions for consent | Signed consent proof with timestamp |
| Article 13-14 | Right to information | Consent notices with full disclosure |
| Article 15 | Right of access | requestGdprExport() — Article 15 export |
| Article 17 | Right to erasure | requestDataErasure() |
| Article 30 | Records of processing | requestDpdpExport() |
Installation
npm install @grantex/dpdp @grantex/sdk@grantex/sdk is a peer dependency. You must install it alongside @grantex/dpdp.
Quick Start
1. Create a Consent Record
import { createConsentRecord } from '@grantex/dpdp';
const record = await createConsentRecord({
grantId: 'grant_abc123',
dataPrincipalId: 'user_456',
dataFiduciaryId: 'org_789',
dataFiduciaryName: 'Acme Corp',
purposes: [
{
purposeId: 'email-access',
name: 'Email Access',
description: 'Read and send emails on behalf of the user',
legalBasis: 'consent',
dataCategories: ['email', 'contacts'],
retentionPeriod: '1 year',
thirdPartySharing: false,
},
],
scopes: ['email:read', 'email:send'],
consentNoticeId: 'notice_v1',
consentNoticeContent: 'We will process your email data...',
consentMethod: 'explicit-click',
processingExpiresAt: new Date('2027-01-01'),
retentionUntil: new Date('2028-01-01'),
proofIpAddress: '192.168.1.1', // Stored as SHA-256 hash
signingKey: ed25519PrivateKey, // Optional Ed25519 CryptoKey
apiKey: process.env.GRANTEX_API_KEY!,
baseUrl: 'https://auth.grantex.dev',
});
console.log(record.recordId); // 'rec_...'
console.log(record.consentNoticeHash); // SHA-256 hex string
console.log(record.consentProof.signature); // Ed25519 signature2. Enforce Purpose Limitation
import { PurposeRegistry, enforcePurpose } from '@grantex/dpdp';
const registry = new PurposeRegistry();
registry.register({
purposeId: 'email-access',
name: 'Email Access',
description: 'Read and send emails on behalf of the user',
requiredScopes: ['email:read', 'email:send'],
legalBasis: 'consent',
dataCategories: ['email', 'contacts'],
retentionPeriod: '1 year',
thirdPartySharing: false,
});
// This will throw PurposeViolationError if scopes are insufficient
enforcePurpose(
['email:read', 'email:send', 'calendar:read'], // grant scopes
'email-access', // purpose to enforce
registry,
);3. Withdraw Consent
import { withdrawConsent } from '@grantex/dpdp';
const confirmation = await withdrawConsent(
'rec_001',
'User no longer wants email processing',
{
revokeGrant: true, // Also revoke the Grantex grant token
deleteProcessedData: true, // Request data deletion
apiKey: process.env.GRANTEX_API_KEY!,
baseUrl: 'https://auth.grantex.dev',
},
);
console.log(confirmation.status); // 'withdrawn'
console.log(confirmation.grantRevoked); // true
console.log(confirmation.withdrawnAt); // Date4. File a Grievance
import { fileGrievance } from '@grantex/dpdp';
const grievance = await fileGrievance(
{
dataPrincipalId: 'user_456',
recordId: 'rec_001',
type: 'unauthorized_processing',
description: 'Agent accessed calendar data without consent',
evidence: { auditEntries: ['audit_entry_789'] },
},
process.env.GRANTEX_API_KEY!,
'https://auth.grantex.dev',
);
console.log(grievance.referenceNumber); // 'GRV-2026-00042'
console.log(grievance.expectedResolutionBy); // 7 days from now5. Export Compliance Reports
import {
requestDpdpExport,
requestGdprExport,
requestEuAiActExport,
} from '@grantex/dpdp';
// DPDP Act audit export
const dpdp = await requestDpdpExport(
{
dateFrom: new Date('2026-01-01'),
dateTo: new Date('2026-03-31'),
format: 'json',
includeActionLog: true,
includeConsentRecords: true,
},
apiKey,
baseUrl,
);
// GDPR Article 15 subject access request
const gdpr = await requestGdprExport(
{
dateFrom: new Date('2026-01-01'),
dateTo: new Date('2026-03-31'),
format: 'json',
includeActionLog: true,
includeConsentRecords: true,
dataPrincipalId: 'user_456',
},
apiKey,
baseUrl,
);
// EU AI Act conformance report
const euAiAct = await requestEuAiActExport(
{
dateFrom: new Date('2026-01-01'),
dateTo: new Date('2026-03-31'),
format: 'json',
includeActionLog: true,
includeConsentRecords: true,
},
apiKey,
baseUrl,
);
console.log(euAiAct.downloadUrl); // Time-limited download URL (24h expiry)API Reference
Consent Records
createConsentRecord(options: CreateConsentRecordOptions): Promise<DPDPConsentRecord>
Create a DPDP consent record linked to a Grantex grant token.
- Validates all mandatory purpose fields
- Computes SHA-256 hash of consent notice content
- Signs the record with Ed25519 if a signing key is provided
- IP addresses are stored as SHA-256 hashes (never plaintext)
getConsentRecord(recordId: string, apiKey: string, baseUrl: string): Promise<DPDPConsentRecord>
Fetch a single consent record by ID.
listConsentRecords(principalId: string, apiKey: string, baseUrl: string): Promise<DPDPConsentRecord[]>
List all consent records for a data principal.
Consent Registry
new ConsentRegistry()
In-memory immutable consent registry with caching.
| Method | Description |
|---|---|
| register(record) | Register a consent record (immutable — cannot be re-registered) |
| get(recordId) | Get a consent record by ID |
| listForPrincipal(principalId) | List records for a data principal |
| markWithdrawn(recordId, reason) | Mark a record as withdrawn |
| getStats() | Get registry statistics |
Consent Notices
createConsentNotice(options: CreateConsentNoticeOptions): Promise<ConsentNotice>
Register a versioned consent notice with the Grantex auth service.
validateNotice(notice: ConsentNotice): string[]
Validate a consent notice has all required fields. Returns an array of error strings (empty if valid).
computeNoticeHash(content: string): Promise<string>
Compute SHA-256 hash of consent notice content. Returns a hex-encoded string.
Withdrawal
withdrawConsent(recordId: string, reason: string, options: WithdrawConsentOptions): Promise<WithdrawalConfirmation>
Withdraw consent for a consent record. Options:
| Option | Type | Description |
|---|---|---|
| revokeGrant | boolean | Also revoke the linked Grantex grant token |
| deleteProcessedData | boolean | Request deletion of data processed under this consent |
| apiKey | string | Grantex API key |
| baseUrl | string | Grantex auth service base URL |
Purpose Registry
new PurposeRegistry()
Maps named purposes to required scopes.
| Method | Description |
|---|---|
| register(purpose) | Register a named purpose with required scopes |
| get(purposeId) | Get a registered purpose |
| listAll() | List all registered purposes |
| getScopesForPurpose(purposeId) | Get scopes required for a purpose |
| toConsentPurpose(purposeId) | Convert to a ConsentPurpose for consent records |
Purpose Enforcement
enforcePurpose(grantScopes: string[], purposeId: string, purposeRegistry: PurposeRegistry): void
Check that a grant token's scopes satisfy a declared processing purpose.
Throws PurposeViolationError if scopes are insufficient.
checkPurposeCompliance(record: DPDPConsentRecord): string[]
Validate that a consent record has proper purpose definitions. Returns an array of error strings (empty if compliant).
Data Principal Rights
getDataPrincipalRecords(principalId: string, apiKey: string, baseUrl: string): Promise<DataPrincipalRecords>
Fetch all consent records belonging to a data principal (DPDP Section 11).
requestDataErasure(principalId: string, apiKey: string, baseUrl: string): Promise<ErasureRequest>
Submit a data erasure request for a data principal (DPDP Section 12).
Grievances
fileGrievance(params: FileGrievanceParams, apiKey: string, baseUrl: string): Promise<Grievance>
File a grievance against a data fiduciary (DPDP Section 13).
getGrievanceStatus(grievanceId: string, apiKey: string, baseUrl: string): Promise<Grievance>
Check the status of a filed grievance.
generateReferenceNumber(): string
Generate a grievance reference number in format GRV-YYYY-NNNNN.
calculateExpectedResolution(fromDate?: Date): Date
Calculate the expected resolution date (7 calendar days from the given date).
Compliance Exports
requestDpdpExport(params, apiKey, baseUrl): Promise<ComplianceExportResult>
Request a DPDP audit export containing consent records, action logs, and summary.
requestGdprExport(params, apiKey, baseUrl): Promise<ComplianceExportResult>
Request a GDPR Article 15 data subject access request export.
requestEuAiActExport(params, apiKey, baseUrl): Promise<ComplianceExportResult>
Request an EU AI Act conformance report covering all defined articles.
getExportStatus(exportId, apiKey, baseUrl): Promise<ComplianceExportResult>
Get the status and download URL of a previously requested export.
EU_AI_ACT_ARTICLES
Constant array of EU AI Act articles covered by the conformance report:
| Article | Title | |---|---| | 9 | Risk Management System | | 10 | Data and Data Governance | | 11 | Technical Documentation | | 12 | Record-keeping | | 13 | Transparency | | 14 | Human Oversight | | 15 | Accuracy, Robustness, Cybersecurity | | 26 | Obligations of Deployers | | 50 | Transparency for GPAI |
Regions
REGION_IN: RegionConfig
India region configuration — DPDP Act settings, 11 supported languages.
REGION_EU: RegionConfig
European Union region configuration — GDPR + EU AI Act settings, 24 supported languages.
REGIONS: Record<string, RegionConfig>
All supported regions keyed by region code.
getRegion(code: string): RegionConfig | undefined
Get region config by region code (case-insensitive).
Errors
All errors extend DpdpError and include a code string and optional statusCode.
| Error Class | Code | Description |
|---|---|---|
| DpdpError | (varies) | Base error class |
| ConsentRequiredError | CONSENT_REQUIRED | No consent record exists for the operation |
| PurposeViolationError | PURPOSE_VIOLATION | Grant scopes do not satisfy the required purpose |
| WithdrawalError | WITHDRAWAL_ERROR | Consent withdrawal failed |
| GrievanceError | GRIEVANCE_ERROR | Grievance filing or retrieval failed |
| ExportError | EXPORT_ERROR | Compliance export failed |
PurposeViolationError additionally exposes a missingScopes: string[] property.
Type Definitions
DPDPConsentRecord
interface DPDPConsentRecord {
recordId: string;
grantId: string;
dataPrincipalId: string;
dataPrincipalDID?: string;
dataFiduciaryId: string;
dataFiduciaryName: string;
purposes: ConsentPurpose[];
scopes: string[];
consentNoticeId: string;
consentNoticeHash: string; // SHA-256 of notice content
consentGivenAt: Date;
consentMethod: 'explicit-click' | 'api-delegated';
processingExpiresAt: Date;
retentionUntil: Date;
consentProof: ConsentProof;
status: 'active' | 'withdrawn' | 'expired';
withdrawnAt?: Date;
withdrawnReason?: string;
lastAccessedAt?: Date;
accessCount: number;
actions: ConsentAction[];
}ConsentPurpose
interface ConsentPurpose {
purposeId: string;
name: string;
description: string;
legalBasis: 'consent' | 'legitimate-interest' | 'contract';
dataCategories: string[];
retentionPeriod: string;
thirdPartySharing: boolean;
thirdParties?: string[];
}ConsentProof
interface ConsentProof {
ipAddress?: string; // SHA-256 hash of IP (never plaintext)
userAgent?: string;
sessionId?: string;
signedAt: Date;
signature: string; // Ed25519 over canonical record payload
}Grievance
interface Grievance {
grievanceId: string;
dataPrincipalId: string;
recordId: string;
type: 'unauthorized_processing' | 'withdrawal_refused' | 'data_breach' | 'other';
description: string;
evidence?: { auditEntries?: string[] };
status: 'submitted' | 'under_review' | 'resolved' | 'escalated';
referenceNumber: string; // GRV-YYYY-NNNNN
expectedResolutionBy: Date; // 7 calendar days
resolvedAt?: Date;
resolution?: string;
}ComplianceExportRequest
interface ComplianceExportRequest {
type: 'dpdp-audit' | 'gdpr-article-15' | 'eu-ai-act-conformance';
dateFrom: Date;
dateTo: Date;
format: 'json' | 'csv';
includeActionLog: boolean;
includeConsentRecords: boolean;
dataPrincipalId?: string;
}ComplianceExportResult
interface ComplianceExportResult {
exportId: string;
type: string;
status: 'complete';
recordCount: number;
data: unknown;
downloadUrl?: string;
downloadExpiresAt?: Date; // 24-hour expiry
}RegionConfig
interface RegionConfig {
regionCode: string;
regionName: string;
dataResidencyRequired: boolean;
consentMinAge: number;
grievanceResolutionDays: number;
defaultLanguage: string;
supportedLanguages: string[];
regulatoryAuthority: string;
regulatoryUrl: string;
}Security Considerations
Consent Record Integrity
Every consent record can be signed with an Ed25519 key. The signature covers a canonical
JSON payload containing grantId, dataPrincipalId, dataFiduciaryId, purposes,
scopes, consentNoticeHash, and consentGivenAt. This makes consent records tamper-evident
and verifiable by any party holding the organization's public key.
PII Protection
IP addresses provided in consent proofs are hashed with SHA-256 before being sent to the server. The raw IP address never leaves the client. Other PII fields like user agent strings are stored as-is but can be omitted.
Consent Notice Hashing
Consent notice content is hashed with SHA-256 and stored alongside the consent record. This ensures that any modification to the notice text after consent was given is detectable.
Immutability
Consent records are immutable after creation. The ConsentRegistry enforces this by freezing
records and rejecting duplicate registrations. Withdrawal creates a new state transition
rather than modifying the original record.
Data Isolation
The ConsentRegistry.listForPrincipal() method ensures a data principal can only access
their own records. Cross-principal access is prevented at both the client registry and
server API levels.
Withdrawal Irreversibility
Once consent is withdrawn, it cannot be undone through the API. A new consent record must be created if processing is to resume.
Related Packages
| Package | Description |
|---|---|
| @grantex/sdk | Core TypeScript SDK for Grantex |
| @grantex/express | Express.js middleware |
| @grantex/gateway | Reverse-proxy gateway |
| @grantex/mcp | MCP server for Claude Desktop |
| @grantex/langchain | LangChain integration |
| @grantex/vercel-ai | Vercel AI SDK integration |
| @grantex/conformance | Conformance test suite |
| @grantex/cli | CLI tool |
License
Apache-2.0. See LICENSE.
