Security Considerations
Pinned data is stored in the database as plaintext JSON. Understanding tenant isolation boundaries, what data is appropriate to pin, and how encryption at rest is applied is critical for compliant use of the pinned data system.
Tenant Isolation
Every read and write operation in PinnedDataService includes TenantId from the execution context. Pinned data from one tenant can never be accessed by another tenant — all queries include a WHERE TenantId = @TenantId clause.
// PinnedDataService.cs — all methods include TenantId
public async Task<string?> GetRawJsonAsync(int processId, string nodeId, CancellationToken ct)
{
return await _db.QuerySingleOrDefaultAsync<string>(@"
SELECT DataJson FROM Process_NodePinnedData
WHERE TenantId = @TenantId -- Hard isolation boundary
AND ProcessId = @ProcessId
AND NodeId = @NodeId
AND (ExpiresAt IS NULL OR ExpiresAt > SYSUTCDATETIME())",
new { TenantId = _tenantContext.TenantId, ProcessId = processId, NodeId = nodeId },
cancellationToken: ct);
}
Pinned Data is Visible in Node Inspector
Any user with access to the Observer Panel for a given execution can see the pinned data for every node in that execution. This is an important consideration for what data you pin.
| Data Type | Safe to Pin? | Notes |
|---|---|---|
| Aggregate counts, totals, metadata | Yes | No personal data — safe to display in Inspector |
| Cached API responses (non-personal) | Yes | Check data classification of the API response |
| AI conversation history | With care | May contain user messages — apply PII classification; consider masking before pinning |
| Personally Identifiable Information (PII) | No | Avoid pinning names, email addresses, national IDs, etc. |
| Authentication tokens or API keys | Never | Use ICredentialResolver for all credentials — never pin secrets |
Encryption at Rest
The Process_NodePinnedData table is subject to Transparent Data Encryption (TDE) at the SQL Server level — the same encryption that applies to all application tables. There is no additional column-level encryption on DataJson. If column-level encryption for sensitive pinned data is required, pre-encrypt the value before passing it to NodeExecutionResult.Success.
// Column-level encryption pattern (if needed)
var sensitiveData = new { SensitiveValue = "..." };
var encrypted = _dataProtector.Protect(JsonSerializer.Serialize(sensitiveData));
return NodeExecutionResult.Success(
output : result,
pinnedData : new { EncryptedPayload = encrypted }
);
API Access Controls
The management API endpoints for clearing pinned data require the process:manage permission. Reading pinned data via the Node Inspector API requires execution:view on the relevant process.
| API Endpoint | Required Permission |
|---|---|
GET /api/executions/{id}/nodes/{nodeId}/inspector | execution:view |
DELETE /api/processes/{processId}/nodes/{nodeId}/pinned-data | process:manage |
DELETE /api/processes/{processId}/pinned-data | process:manage |
Audit Log
Writes to Process_NodePinnedData are logged at the Information level by PinnedDataService with structured fields including tenantId, processId, nodeId, and action (save/clear). These log entries flow through Serilog to Loki for retention.
ICredentialResolver for all API keys, passwords, and tokens. The credential resolver retrieves secrets at runtime from secure storage — pinning them to the database in plaintext defeats the purpose of secure credential management.