Portal Community

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 TypeSafe to Pin?Notes
Aggregate counts, totals, metadataYesNo personal data — safe to display in Inspector
Cached API responses (non-personal)YesCheck data classification of the API response
AI conversation historyWith careMay contain user messages — apply PII classification; consider masking before pinning
Personally Identifiable Information (PII)NoAvoid pinning names, email addresses, national IDs, etc.
Authentication tokens or API keysNeverUse 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 EndpointRequired Permission
GET /api/executions/{id}/nodes/{nodeId}/inspectorexecution:view
DELETE /api/processes/{processId}/nodes/{nodeId}/pinned-dataprocess:manage
DELETE /api/processes/{processId}/pinned-dataprocess: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.

Never pin credentials. Use 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.