UIAP Policy Extension
UIAP Policy Extension v0.1
Section titled “UIAP Policy Extension v0.1”| Field | Value |
|---|---|
| Status | Draft |
| Version | 0.1 |
| Date | 2026-03-27 |
| Dependencies | [UIAP-CORE] |
| Editors | Patrick |
1. Purpose
Section titled “1. Purpose”UIAP Policy Extension v0.1 defines how an application describes and evaluates at runtime machine-readable rules for:
- permitted agent actions,
- confirmations,
- sensitive data,
- redaction,
- audit,
- human handoff.
The Extension builds on UIAP Core v0.1, Capability Model v0.1, Web Profile v0.1, and Action Runtime v0.1.
Normative Terms
Section titled “Normative Terms”The key words MUST, MUST NOT, SHOULD, MAY in this document are to be interpreted as described in RFC 2119 and BCP 14, when and only when they appear in ALL CAPS.
2. Identifier and Negotiation
Section titled “2. Identifier and Negotiation”type ExtensionId = "uicp.policy";type ExtensionVersion = "0.1";An implementation using this extension MUST negotiate it during the handshake:
{ "id": "uicp.policy", "versions": ["0.1"], "required": false}3. Scope and Principles
Section titled “3. Scope and Principles”- Policy is locally enforceable. An app or bridge MUST be able to enforce a policy decision even when the agent wants something different.
- Policy is decision-oriented, not prompt-oriented.
- Policy separates permission, risk, sensitivity, audit obligation, and handoff obligation.
- Policy MUST be evaluated before every non-trivial Action.
- Policy MUST NOT be weaker than browser or platform boundaries.
4. Core Concepts
Section titled “4. Core Concepts”4.1 Principals
Section titled “4.1 Principals”type PrincipalType = "user" | "agent" | "bridge" | "observer" | "system";
interface PolicyPrincipal { type: PrincipalType; id: string; roles?: string[]; grants?: PolicyGrant[];}4.2 Grants
Section titled “4.2 Grants”type PolicyGrant = | "observe" | "guide" | "draft" | "act" | "admin" | "read.sensitive" | "read.secret" | "write.sensitive" | "billing" | "identity" | "security";Semantics
Section titled “Semantics”observe: Read the UI but change nothingguide: Highlight/focus/navigate without side effectsdraft: Reversible drafts or field suggestionsact: Normal operational actionsadmin: Privileged actions- Additional grants govern sensitive domains and data
4.3 Data Classes
Section titled “4.3 Data Classes”type DataClass = | "public" | "internal" | "personal" | "sensitive" | "credential" | "secret" | "payment" | "legal";4.4 Side-Effect Classes
Section titled “4.4 Side-Effect Classes”type SideEffectClass = | "none" | "local_ui" | "internal_persist" | "external_message" | "identity_change" | "billing_change" | "security_change" | "irreversible";4.5 Policy Decisions
Section titled “4.5 Policy Decisions”type PolicyEffect = | "allow" | "confirm" | "deny" | "handoff";4.6 Reason Codes
Section titled “4.6 Reason Codes”type PolicyReasonCode = | "grant_missing" | "route_denied" | "target_denied" | "risk_confirm" | "risk_blocked" | "sensitive_data" | "secret_data" | "credential_data" | "external_effect" | "privileged_action" | "user_activation_missing" | "human_actor_required" | "unsafe_retry" | "redaction_required" | "policy_default";5. Policy Document
Section titled “5. Policy Document”interface PolicyDocument { modelVersion: "0.1"; extension: "uicp.policy"; profile?: string; // e.g. "web@0.1"
defaults: PolicyDefaults; rules: PolicyRule[];
redaction?: RedactionRule[]; audit?: AuditPolicy; handoff?: HandoffPolicy;
metadata?: Record<string, unknown>;}interface PolicyDefaults { onSafeRisk: PolicyEffect; // RECOMMENDED: "allow" onConfirmRisk: PolicyEffect; // RECOMMENDED: "confirm" onBlockedRisk: PolicyEffect; // RECOMMENDED: "handoff" onUnknownAction: PolicyEffect; // RECOMMENDED: "deny" onSensitiveRead: PolicyEffect; // RECOMMENDED: "confirm" onSecretRead: PolicyEffect; // RECOMMENDED: "deny"}5.1 Policy Rules
Section titled “5.1 Policy Rules”interface PolicyRule { id: string; enabled?: boolean; priority?: number; // higher wins
when: PolicyPredicate; effect: PolicyEffect;
obligations?: PolicyObligation[]; reason?: string;}interface PolicyPredicate { actionIds?: string[]; routeIds?: string[]; stableIds?: string[]; roles?: UIRole[];
riskLevels?: RiskLevel[]; riskTags?: RiskTag[];
dataClasses?: DataClass[]; sideEffectClasses?: SideEffectClass[];
principals?: string[]; // principal.id principalTypes?: PrincipalType[]; requiredGrants?: PolicyGrant[];
executionModes?: ExecutionMode[];}5.2 Obligations
Section titled “5.2 Obligations”type PolicyObligation = | { type: "audit"; level?: AuditLevel; } | { type: "redact"; paths: string[]; replacement?: string; } | { type: "limitExecutionModes"; modes: ExecutionMode[]; } | { type: "requireVerification"; policy: "any" | "all"; signals?: SuccessSignal[]; } | { type: "requireUserActivation"; } | { type: "requireHumanActor"; reason?: string; } | { type: "maxAttempts"; value: number; };6. Policy Context
Section titled “6. Policy Context”interface PolicyContext { sessionId?: string; revision?: RevisionId;
principal: PolicyPrincipal;
actionId: ActionId; target?: { ref?: TargetRef; stableId?: StableId; role?: UIRole; name?: string; scopeId?: ScopeId; documentId?: DocumentId; };
risk?: RiskDescriptor; dataClasses?: DataClass[]; sideEffectClass?: SideEffectClass;
executionMode?: ExecutionMode; routeId?: string;
userActivation?: { isActive?: boolean; hasBeenActive?: boolean; };
retryOfActionHandle?: string; attempt?: number;
args?: Record<string, unknown>; metadata?: Record<string, unknown>;}7. Policy Decision
Section titled “7. Policy Decision”interface PolicyDecision { decision: PolicyEffect; reasonCodes: PolicyReasonCode[];
obligations?: PolicyObligation[]; effectiveExecutionModes?: ExecutionMode[];
redactions?: RedactionPlan[]; audit?: AuditDirective;
cacheTtlMs?: number;}interface RedactionPlan { path: string; replacement: string;}type AuditLevel = "none" | "decision" | "result" | "full";
interface AuditDirective { level: AuditLevel; emitRecord: boolean;}8. Message Types
Section titled “8. Message Types”8.1 uicp.policy.get
Section titled “8.1 uicp.policy.get”Request
Section titled “Request”interface PolicyGetPayload {}Response: uicp.policy.document
Section titled “Response: uicp.policy.document”interface PolicyDocumentPayload { policy: PolicyDocument; revision?: string;}8.2 uicp.policy.evaluate
Section titled “8.2 uicp.policy.evaluate”Request
Section titled “Request”interface PolicyEvaluatePayload { context: PolicyContext;}Response: uicp.policy.decision
Section titled “Response: uicp.policy.decision”interface PolicyDecisionPayload { contextHash?: string; decision: PolicyDecision;}8.3 uicp.policy.changed
Section titled “8.3 uicp.policy.changed”interface PolicyChangedPayload { revision: string; reason?: "role_change" | "tenant_change" | "feature_flag" | "policy_update" | string; policy: PolicyDocument;}8.4 uicp.policy.audit
Section titled “8.4 uicp.policy.audit”interface PolicyAuditPayload { record: PolicyAuditRecord;}interface PolicyAuditRecord { auditId: string; ts: string; sessionId?: string;
principal: PolicyPrincipal; actionId?: ActionId; target?: TargetRef;
decision: PolicyEffect; reasonCodes: PolicyReasonCode[];
obligations?: PolicyObligation[]; sideEffectClass?: SideEffectClass;
outcome?: "preflight" | "granted" | "confirmed" | "executed" | "failed" | "denied" | "handoff"; stateRevision?: RevisionId;
metadata?: Record<string, unknown>;}9. Evaluation Order
Section titled “9. Evaluation Order”A Policy Executor MUST derive decisions in this order:
- Explicit deny rules
- Grant check
- Target/route blocklists
- Data classes and redaction obligations
- Risk level and risk tags
- Side-effect class
- User activation / human actor obligations
- Defaults
Recommended Safe Baseline
Section titled “Recommended Safe Baseline”safe+ sufficient grants ->allowconfirm->confirmblocked->handoffsecret/credentialread without special grant ->deny- Non-idempotent retry with
sideEffectState="unknown"->denyorhandoff
10. Redaction Model
Section titled “10. Redaction Model”Redaction MUST be modeled separately from action permissibility.
interface RedactionRule { id: string; when: { dataClasses?: DataClass[]; stableIds?: StableId[]; routeIds?: string[]; }; applyTo: Array<"snapshot" | "signal" | "returnValue" | "audit">; replacement?: string; // default: "[REDACTED]"}- Redaction MAY partially mask an object without denying the Action itself.
credentialandsecretSHOULD be redacted by default.- Audit data SHOULD be redacted more aggressively than runtime data.
11. Handoff Model
Section titled “11. Handoff Model”Browsers and platforms require genuine user interaction or trusted input in certain cases. Therefore, handoff is not an error but a normal policy outcome. navigator.userActivation provides the activation state, Event.isTrusted distinguishes browser/user-agent-generated events from dispatchEvent() events, and HTMLElement.click() does not automatically replace every user-activation-gated situation. ([MDN Web Docs][2])
interface HandoffPolicy { triggers: HandoffTrigger[]; defaultMessage?: string;}type HandoffTrigger = | "user_activation_required" | "credential_entry" | "payment_approval" | "external_auth" | "captcha" | "legal_acknowledgement" | "ambiguity" | "security_sensitive";- When an obligation contains
requireUserActivationanduserActivation.isActive !== true, the decision MUST be at leasthandoff. - When
requireHumanActoris active, no autonomous execution MAY follow. handoffSHOULD be able to produce a human-readable explanation text.
12. Minimal Conformance Scope
Section titled “12. Minimal Conformance Scope”A conforming uicp.policy@0.1 implementation MUST:
- Support
uicp.policy.getanduicp.policy.evaluate, - Validate
PolicyDocumentandPolicyDecision, - Distinguish between
confirm,deny,handoff, andallow, - Be able to apply Redaction Rules,
- Be able to produce Audit Records,
- Treat
blockedrisk more strictly thanconfirm.
13. Example Policy
Section titled “13. Example Policy”{ "modelVersion": "0.1", "extension": "uicp.policy", "profile": "web@0.1", "defaults": { "onSafeRisk": "allow", "onConfirmRisk": "confirm", "onBlockedRisk": "handoff", "onUnknownAction": "deny", "onSensitiveRead": "confirm", "onSecretRead": "deny" }, "rules": [ { "id": "deny-credentials", "priority": 100, "when": { "dataClasses": ["credential", "secret"] }, "effect": "deny", "obligations": [ { "type": "audit", "level": "decision" } ] }, { "id": "confirm-create-video", "priority": 50, "when": { "actionIds": ["video.create"] }, "effect": "confirm", "obligations": [ { "type": "requireVerification", "policy": "all", "signals": [ { "kind": "route.changed", "pattern": "/videos/:id" }, { "kind": "toast.contains", "text": "erstellt" } ] }, { "type": "audit", "level": "result" } ] } ], "redaction": [ { "id": "mask-secrets", "when": { "dataClasses": ["secret", "credential"] }, "applyTo": ["snapshot", "audit", "returnValue"], "replacement": "[REDACTED]" } ], "audit": { "level": "result", "includeArgs": false, "includeReturnValue": false }, "handoff": { "triggers": [ "user_activation_required", "credential_entry", "payment_approval", "external_auth" ], "defaultMessage": "Please complete this step yourself." }}Normative References
Section titled “Normative References”- [UIAP-CORE] UIAP Core v0.1
- [RFC2119] Key words for use in RFCs to Indicate Requirement Levels, BCP 14
Security Considerations
Section titled “Security Considerations”- Policy documents SHOULD only be loaded from trusted sources.
- A local
denydecision MUST always take precedence and MUST NOT be overridden by an external service. - Audit logs MUST be stored in a tamper-proof manner.
- Sensitive data MUST be redacted before logging.
requireUserActivationSHOULD be set for all irreversible or externally effective actions.
Changelog
Section titled “Changelog”| Version | Date | Changes |
|---|---|---|
| 0.1 | 2026-03-27 | Initial draft |