Security
Happen provides the minimal, essential tools that enable developers to build their own security models.
1. Event Identity and Integrity Tooling
Event ID Generation: Every event in Happen automatically receives a unique, cryptographically secure identifier generated by the framework.
Automatic Event Hash: The framework automatically calculates and includes a cryptographic hash of each event's contents in its causal context:
// Example of what an event looks like internally
{
type: "payment-processed",
payload: { amount: 100 },
context: {
causal: {
id: "evt-789", // Unique ID
hash: "a1b2c3...", // Cryptographic hash
sender: "payment-node",
causationId: "evt-456",
correlationId: "order-123"
}
}
}
This hash is calculated from the canonicalized event (without the hash itself) using a hashing algorithm provided by the runtime environment.
2. Node Identity
Identity Container: Every node in Happen has an inherent identity established at creation:
// Node identity is established automatically
const paymentNode = createNode('payment-service');
This identity is used to track the origin of events and forms the foundation of security verification.
Identity Propagation: Node identity is automatically included in the causal context of emitted events:
// The framework automatically includes the sender information
// in the causal context of every event
orderNode.send(paymentNode, {
type: "process-payment",
payload: { amount: 100 }
});
// Resulting event includes sender identity
// {
// type: "process-payment",
// payload: { amount: 100 },
// context: {
// causal: {
// sender: "order-service", // Added automatically
// ...
// }
// }
// }
Origin Context: For tracking the original source of events (not just the immediate sender):
// Adding origin information to events
orderNode.send(paymentNode, {
type: "process-payment",
payload: { amount: 100 },
context: {
origin: {
sourceId: "user-123", // Original source (e.g., user ID)
sourceType: "user" // Type of source
}
}
});
This allows developers to build their own identity models on top of Happen.
3. Causal Chain
The causal relationships between events provide a natural foundation for security verification:
// Events naturally form a causal chain
event.context.causal = {
id: "evt-789",
sender: "user-service",
causationId: "evt-456", // Links to the event that caused this one
correlationId: "session-123" // Groups related events
};
This causality enables:
Tracking the complete flow of events
Verifying that events occurred in the expected sequence
Building audit trails based on causal relationships
Detecting replay or injection attacks
4. The Event Continuum for Verification
The Event Continuum provides a natural place to implement security verification:
// Security verification using the Event Continuum
paymentNode.on("process-payment", function verifyAndProcess(event, context) {
// Extract security information
const { signature } = event.context.integrity || {};
const { hash } = event.context.causal;
// Verify using runtime crypto
if (signature) {
const senderPublicKey = getPublicKey(event.context.causal.sender);
if (!verifySignature(hash, signature, senderPublicKey)) {
return {
success: false,
reason: "invalid-signature"
};
}
}
// Continue to payment processing
return processPayment;
});
This allows security verification to integrate seamlessly with the normal event flow.
How These Enable Security
By providing building blocks rather than implementations, Happen enables developers to build their own security models:
Integrity Verification
// Using the automatic hash for integrity verification
paymentNode.on("process-payment", (event, context) => {
// Hash verification happens automatically by the framework
// Can perform additional custom verification if needed
if (!customVerification(event)) {
return { success: false, reason: "verification-failed" };
}
// Continue processing
return processPayment;
});
Digital Signatures and Tamper Evidence
// Signing events using runtime crypto
orderNode.on("create-order", (event, context) => {
// Generate order and prepare payment event
const paymentEvent = {
type: "process-payment",
payload: {
orderId: generateOrderId(),
amount: calculateTotal(event.payload.items)
},
context: {
// Add signature using runtime crypto
integrity: {
signature: signWithRuntimeCrypto(
event.context.causal.hash,
getPrivateKey()
)
}
}
};
// Send signed event
return orderNode.send(paymentNode, paymentEvent);
});
Authorization Patterns
// Authorization using the Event Continuum
resourceNode.on("access-resource", (event, context) => {
// Extract source information
const { sourceId, sourceType } = context.origin || {};
// Check permissions
if (!hasPermission(sourceId, event.payload.resourceId, "read")) {
return {
success: false,
reason: "unauthorized",
message: "User does not have permission to access this resource"
};
}
// Continue to resource access
return provideResource;
});
Multi-tenant Isolation
// Tenant isolation using origin information
dataNode.on("query-data", (event, context) => {
// Extract tenant from origin
const tenantId = extractTenantFromSource(context.origin?.sourceId);
// Enforce tenant isolation
const tenantScopedQuery = {
...event.payload.query,
tenantId // Add tenant filter to query
};
// Execute scoped query
const results = executeQuery(tenantScopedQuery);
return { results };
});
End-to-End Encryption
// Encrypting sensitive payload data
userNode.on("send-sensitive-message", (event, context) => {
// Get recipient's public key
const recipientPublicKey = getPublicKey(event.payload.recipientId);
// Create encrypted message
const encryptedMessage = encryptWithRuntimeCrypto(
event.payload.sensitiveContent,
recipientPublicKey
);
// Send message with encrypted content
return userNode.send(messagingNode, {
type: "deliver-message",
payload: {
recipientId: event.payload.recipientId,
encryptedContent: encryptedMessage
}
});
});
Minimal but Powerful
The key is to expose the right features in the core event system, without baking in specific security implementations by giving developers all the building blocks they need without restricting their options.
These features enable developers to implement:
Digital signatures for event verification
Role-based access control for node interactions
Attribute-based access policies
Multi-layer security models
Custom authentication mechanisms
Auditing and compliance systems
End-to-end encryption for sensitive data
While keeping the core framework minimal and focused on its primary responsibility: the flow of events between nodes.
Last updated