Appearance
Data Motion Pipelines
A unified, extensible model for governing clipboard, drag-and-drop, and screen capture/sharing using declarative pipelines. Rules select when data may move, and actions execute pipelines composed of builtin or external processors for inspection and transformation.
What You'll Learn
- One model for all channels: clipboard, dragDrop, screenCapture, screenShare, and custom channels
- Deterministic, priority-based rule selection
- First-class external integrations (HTTP/gRPC/script/queue) for OCR, DLP, AV, redaction, etc.
- Builtins exposed as processors (e.g., builtin.htmlSanitize, builtin.stripFormats, builtin.watermark)
- Strong security controls: mTLS, pinning, CRED-bound secrets, payload minimization, sovereign region enforcement
Concepts
Channels (extensible):
- clipboard — bidirectional text/rich content/file-list exchange
- dragDrop — bidirectional file/object moves between host and sandbox
- screenCapture — outbound image capture (screenshots)
- screenShare — outbound screen streaming (screen share)
- ai.promptInput — prompts and inputs sent into AI models or agents
- ai.modelOutput — AI model or agent responses and generated content
- rag.contextRetrieval — retrieval queries and retrieved context used in RAG workflows
- workflowTransfer — orchestration payloads moving between tools, agents, and workflow steps
- custom — vendor-specific future channels (e.g., custom:voice, custom:ocr)
Settings (hard switches, defaults)
json
{
"configuration": { "launch": { "runtime": {
"dataMotion": {
"settings": {
"clipboard": { "enabled": true, "defaultAction": "deny" },
"dragDrop": { "enabled": true, "defaultAction": "deny" },
"screenCapture": { "enabled": true, "mode": "foreground-only", "watermark": { "text": "CONFIDENTIAL", "opacity": 0.2 }, "blurBackground": true },
"screenShare": { "enabled": false, "mode": "disabled" },
"audit": { "logDecisions": true, "sampleRate": 1.0 }
}
}
} } }
}If
settings.<channel>.enabledis false, the channel is denied regardless of rules.If enabled and no rule matches,
defaultActionapplies (recommenddeny).Rules (additive across scopes; highest priority wins)
json
{
"id": "rule-id",
"enabled": true,
"priority": 900,
"channel": "clipboard",
"direction": "out", // in | out | both (screen* treated as out)
"match": {
"mimeTypes": [ { "pattern": "image/*", "patternType": "glob" } ],
"sizeBytes": { "max": 1048576 },
"imageMegapixels": { "min": 0.25, "max": 2.0 },
"imageDimensions": { "width": { "max": 2560 }, "height": { "max": 1440 } },
"dlpFindings": { "provider": "purview", "severities": ["high"], "match": "any" },
"entities": { "pii": ["Email", "SSN"], "minConfidence": 0.7 },
"ocr": { "present": true, "language": ["en"] }
},
"action": { "type": "allow" | "deny" | "process" }
}Match: Image-Specific Fields
Two additional match fields target image payloads on the clipboard, drag-and-drop, and screen capture channels:
imageMegapixels— Match by total pixel count (width × height ÷ 1 000 000). Supportsminand/ormax. Useful for tiered policies that allow thumbnails, inspect medium images, and deny large captures.imageDimensions— Match by individual width and/or height in pixels. Each axis supportsminand/ormax. Useful for detecting multi-monitor screenshots or unusually shaped captures.
Both fields are evaluated only when the payload is a decoded image. Non-image payloads never match these fields. When combined with mimeTypes, both conditions must pass (logical AND).
Actions: Process (Pipelines)
The process action composes one or more processors executed in order. Each step can inspect, transform, or observe the payload.
json
{
"action": {
"type": "process",
"pipeline": [
{ "processorRef": "dlp-purview", "mode": "inspect" },
{ "processorRef": "builtin.htmlSanitize", "mode": "transform", "with": { "allowElements": [], "allowAttributes": [], "maxDepth": 5 } }
],
"onProcessorFailure": "deny", // deny | allow | skipStep
"cacheTtlSec": 0, // optional cache of inspect results
"audit": "all" // all | deny | none
}
}Processor modes
inspect — The processor reads the payload and returns findings (for example, DLP classifications or entities) that policy can use when deciding whether to allow or deny the operation. Inspect steps must not modify the payload.
transform — The processor may modify the payload (for example, sanitize HTML, strip formats, or redact faces). Subsequent processors and the final sink see the transformed payload.
observe — The processor receives a copy of the payload solely to extract metadata for the audit log (
semanticFindings). Observe steps cannot modify the data or advise a deny for the current operation, and they may run asynchronously or off the critical user path so they do not add user-visible latency.Builtins are modeled as processors (examples):
builtin.stripFormats { strip: ["html","rtf"] }builtin.htmlSanitize { allowElements, allowAttributes, maxDepth }builtin.reencode { from: "rtf", to: "text" }builtin.watermark { text, opacity, style }builtin.blurBackground { enabled: true }builtin.rateLimit { bytesPerSecond, burstBytes }builtin.redact { mode: "hash|blank|mask" }— transform-mode processor that replaces DLP finding locations (field paths or byte ranges) with hashed, blanked, or masked values. Intended asredactProcessorin an agent transfer gatedlpblock.
Note: Previous separate transform or rateLimit action types are replaced by pipelines. Use the corresponding builtin processors instead.
External Processors (Global Registry)
Declare processors globally under configuration.integrations.processors. Supported types: builtin, http, grpc, script, queue.
json
{
"configuration": {
"integrations": {
"processors": [
{
"id": "dlp-purview",
"type": "http",
"endpoint": "https://dlp.api.corp/inspect",
"auth": { "type": "oidc", "clientId": "{{CRED:Purv:clientId}}", "clientSecret": "{{CRED:Purv:secret}}", "tokenUrl": "https://login.corp/token" },
"security": { "mtls": true, "pinSha256": ["ABCD…"] },
"capabilities": ["inspect"],
"dataPolicies": { "sendRaw": true, "maxBytes": 1048576 },
"limits": { "timeoutMs": 2000, "offlinePolicy": "deny" }
}
]
}
}
}Security and privacy:
- Bind credentials via CRED variables; use mTLS + pinning; restrict regions with
regionAllowlist. - Minimize payloads (hashOnly, maxBytes, redactPatterns, chunking) before egress.
- Emit rich audit without leaking payloads (redactRequest/Response).
See: External Processors for full schema and options.
Examples
- Allow outbound plain text (≤ 64 KB); sanitize HTML/RTF elsewhere; deny images
json
{
"configuration": { "launch": { "runtime": { "dataMotion": {
"settings": { "clipboard": { "enabled": true, "defaultAction": "deny" } },
"rules": [
{ "id": "clip-txt-out", "enabled": true, "priority": 900, "channel": "clipboard", "direction": "out", "match": { "mimeTypes": [ { "pattern": "text/plain", "patternType": "exact" } ], "sizeBytes": { "max": 65536 } }, "action": { "type": "allow" } },
{ "id": "clip-strip-rich", "enabled": true, "priority": 850, "channel": "clipboard", "direction": "both", "action": { "type": "process", "pipeline": [ { "processorRef": "builtin.stripFormats", "mode": "transform", "with": { "strip": ["html", "rtf"] } } ] } },
{ "id": "clip-deny-images", "enabled": true, "priority": 800, "channel": "clipboard", "direction": "both", "match": { "mimeTypes": [ { "pattern": "image/*", "patternType": "glob" } ] }, "action": { "type": "deny" } }
]
} } } }
}- Rate-limit clipboard transfers via builtin processor
json
{
"configuration": { "launch": { "runtime": { "dataMotion": {
"rules": [
{ "id": "clip-rate", "enabled": true, "priority": 750, "channel": "clipboard", "direction": "both", "action": { "type": "process", "pipeline": [ { "processorRef": "builtin.rateLimit", "mode": "transform", "with": { "bytesPerSecond": 8192, "burstBytes": 16384 } } ] } }
]
} } } }
}- Per-app: block clipboard to host from Chrome
json
{
"configuration": { "launch": { "runtime": { "dataMotion": {
"rules": [ { "id": "block-chrome-out", "enabled": true, "priority": 920, "channel": "clipboard", "direction": "out",
"match": { "processMatch": [ { "type": "targetPath", "pattern": "**\\\\chrome.exe", "patternType": "glob" } ] },
"action": { "type": "deny" } } ]
} } } }
}- Classification-aware deny (normalized labels)
json
{
"configuration": { "launch": { "runtime": { "dataMotion": {
"settings": { "clipboard": { "enabled": true, "defaultAction": "deny" } },
"rules": [
{
"id": "clip-deny-cui-itar-out",
"enabled": true,
"priority": 1000,
"channel": "clipboard",
"direction": "out",
"match": { "classifications": { "normalized": ["CUI", "CUI//SP-CTI", "ITAR"] } },
"action": { "type": "deny" }
}
]
} } } }
}- Progressive image policy: allow thumbnails, inspect medium images, deny large captures
json
{
"configuration": { "launch": { "runtime": { "dataMotion": {
"settings": { "clipboard": { "enabled": true, "defaultAction": "deny" } },
"rules": [
{
"id": "img-thumb-allow",
"enabled": true,
"priority": 900,
"channel": "clipboard",
"direction": "out",
"match": {
"mimeTypes": [ { "pattern": "image/*", "patternType": "glob" } ],
"imageMegapixels": { "max": 0.25 }
},
"action": { "type": "allow" }
},
{
"id": "img-medium-inspect",
"enabled": true,
"priority": 850,
"channel": "clipboard",
"direction": "out",
"match": {
"mimeTypes": [ { "pattern": "image/*", "patternType": "glob" } ],
"imageMegapixels": { "min": 0.25, "max": 2.0 }
},
"action": {
"type": "process",
"pipeline": [
{ "processorRef": "dlp-ocr", "mode": "inspect" }
],
"onProcessorFailure": "deny"
}
},
{
"id": "img-large-deny",
"enabled": true,
"priority": 800,
"channel": "clipboard",
"direction": "out",
"match": {
"mimeTypes": [ { "pattern": "image/*", "patternType": "glob" } ],
"imageMegapixels": { "min": 2.0 }
},
"action": { "type": "deny" }
}
]
} } } }
}- Asymmetric image clipboard: allow paste-in, deny copy-out
json
{
"configuration": { "launch": { "runtime": { "dataMotion": {
"settings": { "clipboard": { "enabled": true, "defaultAction": "deny" } },
"rules": [
{
"id": "img-in-allow",
"enabled": true,
"priority": 900,
"channel": "clipboard",
"direction": "in",
"match": { "mimeTypes": [ { "pattern": "image/*", "patternType": "glob" } ] },
"action": { "type": "allow" }
},
{
"id": "img-out-deny",
"enabled": true,
"priority": 900,
"channel": "clipboard",
"direction": "out",
"match": { "mimeTypes": [ { "pattern": "image/*", "patternType": "glob" } ] },
"action": { "type": "deny" }
}
]
} } } }
}- Deny clipboard images exceeding single-monitor dimensions
json
{
"configuration": { "launch": { "runtime": { "dataMotion": {
"settings": { "clipboard": { "enabled": true, "defaultAction": "deny" } },
"rules": [
{
"id": "img-single-monitor-allow",
"enabled": true,
"priority": 900,
"channel": "clipboard",
"direction": "out",
"match": {
"mimeTypes": [ { "pattern": "image/*", "patternType": "glob" } ],
"imageDimensions": { "width": { "max": 2560 }, "height": { "max": 1440 } }
},
"action": { "type": "allow" }
},
{
"id": "img-multimonitor-deny",
"enabled": true,
"priority": 850,
"channel": "clipboard",
"direction": "out",
"match": { "mimeTypes": [ { "pattern": "image/*", "patternType": "glob" } ] },
"action": { "type": "deny" }
}
]
} } } }
}- ScreenCapture pipeline: face redaction via gRPC, fallback to blur + watermark
json
{
"configuration": { "launch": { "runtime": { "dataMotion": {
"rules": [
{
"id": "sc-face-redact",
"enabled": true,
"priority": 900,
"channel": "screenCapture",
"direction": "out",
"action": {
"type": "process",
"pipeline": [
{ "processorRef": "grpc.faceRedact", "mode": "transform", "onFailure": "skipStep" },
{ "processorRef": "builtin.blurBackground", "mode": "transform" },
{ "processorRef": "builtin.watermark", "mode": "transform", "with": { "text": "CONFIDENTIAL", "opacity": 0.2 } }
],
"onProcessorFailure": "deny"
}
}
]
} } } }
}- DLP inspection on workflow transfers between agent steps
json
{
"configuration": { "launch": { "runtime": { "dataMotion": {
"settings": { "workflowTransfer": { "enabled": true, "defaultAction": "deny" } },
"rules": [
{
"id": "wf-transfer-dlp",
"enabled": true,
"priority": 900,
"channel": "workflowTransfer",
"direction": "out",
"action": {
"type": "process",
"pipeline": [
{ "processorRef": "dlp-purview", "mode": "inspect" }
],
"onProcessorFailure": "deny"
}
},
{
"id": "wf-transfer-high-risk-approval",
"enabled": true,
"priority": 1000,
"channel": "workflowTransfer",
"direction": "out",
"match": { "dlpFindings": { "provider": "purview", "severities": ["high"], "match": "any" } },
"action": { "type": "deny" }
}
]
} } } }
}- Observe AI prompts without blocking the interaction
json
{
"configuration": { "launch": { "runtime": { "dataMotion": {
"rules": [
{
"id": "ai-prompt-observe",
"enabled": true,
"priority": 800,
"channel": "ai.promptInput",
"direction": "out",
"action": {
"type": "process",
"pipeline": [
{ "processorRef": "ai-telemetry", "mode": "observe" }
],
"onProcessorFailure": "allow"
}
}
]
} } } }
}Evaluation Semantics
- If
settings.<channel>.enabledis false, deny the operation before evaluating rules. - Collect all enabled rules from Global + matching allow policies + the selected Launch Profile whose
matchpasses for the event’s channel and direction. - If no rule matches, apply the channel
defaultActionwhen the channel is enabled. - When rules exist, select the rule with highest
priority(descending). Ties break byid(alphabetical). - Apply
action:denyshort-circuits.allowpermits.processruns the pipeline; per-steponFailureapplies, then globalonProcessorFailure.
Evaluation flow
Latency & UX for Data Motion Pipelines
Data Motion rules often run on latency-sensitive channels such as clipboard, drag-and-drop, and screen capture. When using external processors (HTTP/gRPC/script/queue), treat them as remote dependencies that may be slow or unavailable.
Recommended patterns:
- Keep the UX path shallow: Use lightweight, local or builtin processors on latency-sensitive paths and reserve heavy external inspection for high-risk or large payloads.
- Use size-based routing: Prefer rules that allow small, low-risk transfers synchronously while sending larger or high-risk transfers through inspection pipelines.
- Bound latency: Configure
limits.timeoutMson external processors and chooselimits.offlinePolicyandaction.process.onProcessorFailureso that outages fail in a predictable way. - Cache repeat decisions: When appropriate, use
action.process.cacheTtlSecto reuse recent inspection results for the same payload or classification context instead of re-calling remote services on every operation.
Example (clipboard, outbound; small text allowed synchronously, larger payloads inspected with a bounded-latency processor):
json
{
"configuration": { "launch": { "runtime": { "dataMotion": {
"settings": { "clipboard": { "enabled": true, "defaultAction": "deny" } },
"rules": [
{
"id": "clip-plain-text-small",
"enabled": true,
"priority": 900,
"channel": "clipboard",
"direction": "out",
"match": {
"mimeTypes": [ { "pattern": "text/plain", "patternType": "exact" } ],
"sizeBytes": { "max": 65536 }
},
"action": { "type": "allow" }
},
{
"id": "clip-dlp-inspect-large",
"enabled": true,
"priority": 850,
"channel": "clipboard",
"direction": "out",
"match": { "sizeBytes": { "max": 1048576 } },
"action": {
"type": "process",
"pipeline": [
{ "processorRef": "dlp-purview", "mode": "inspect" }
],
"onProcessorFailure": "deny",
"cacheTtlSec": 300
}
}
]
} } } }
}This pattern ensures simple clipboard operations complete quickly, while larger transfers are inspected with a bounded remote call and optional caching.
Auditing Decisions
When Audit Trails is enabled, each decision includes: channel, direction, matched rule id/priority/scope, whether the channel defaultAction applied, basic metrics (bytes/items), pipeline summary (processor ids, timings, status), optional redacted provider findings, and any semanticFindings produced by observe-mode processors when configured.
Where to Configure
- Global (all launches):
configuration.launch.runtime.dataMotion - Per-app (when that app launches):
apps[].modifications.runtime.dataMotion - Per-launch profile:
apps[].profiles[].runtime.dataMotion
Using Sites with dataMotion
- Sites are active when
configuration.sites.profilesis non-empty. If empty or missing, Sites are inactive. - Add
siteRefsto dataMotionrulesto scope behavior to the selected site. - Declare site-specific processors (same
id) withsiteRefsto override endpoints and policies per site.
Example (clipboard outbound; Shanghai uses in-region DLP endpoint):
json
{
"configuration": {
"sites": {
"selectionMode": "user",
"profiles": [ { "id": "san-jose", "displayName": "San Jose" }, { "id": "shanghai", "displayName": "Shanghai" } ]
},
"integrations": { "processors": [
{ "id": "dlp-http", "type": "http", "endpoint": "https://dlp.us.example.com/inspect", "capabilities": ["inspect"] },
{ "id": "dlp-http", "siteRefs": ["shanghai"], "type": "http", "endpoint": "https://dlp.cn.example.com/inspect", "capabilities": ["inspect"] }
] },
"launch": { "runtime": { "dataMotion": {
"settings": { "clipboard": { "enabled": true, "defaultAction": "deny" } },
"rules": [
{ "id": "clip-allow-txt", "enabled": true, "priority": 900, "channel": "clipboard", "direction": "out",
"match": { "mimeTypes": [ { "pattern": "text/plain", "patternType": "exact" } ], "sizeBytes": { "max": 65536 } },
"action": { "type": "allow" } },
{ "id": "clip-dlp-cn", "enabled": true, "priority": 900, "siteRefs": ["shanghai"], "channel": "clipboard", "direction": "out",
"action": { "type": "process", "pipeline": [ { "processorRef": "dlp-http", "mode": "inspect" } ], "onProcessorFailure": "deny" } }
]
} } }
}
}