Capability UI Protocol / Full Spec
API
Capability UI Protocol · CUP-001 · version 0.1

Permission-aware software with generated interfaces.

This specification defines a library for exposing data and tools as typed capabilities, resolving policy into authorized views, allowing agents to compose presentation, and enforcing the final action at the execution boundary.

Draft standard · reference implementation: TypeScript

1. Goals and non-goals

CUP is the authorization and presentation contract between applications and agents. It makes available actions explicit without prescribing one visual language.

Goals

  • Represent data and tools with stable, typed contracts.
  • Distinguish discovery, inspection, reading, mutation, execution, sharing, and delegation.
  • Generate a safe capability view for an agent or renderer.
  • Express scope, purpose, expiration, approval, and risk.
  • Enforce the exact proposed action immediately before side effects.
  • Produce receipts that explain decisions and results.
  • Support web, mobile, voice, native, and future renderers.

Non-goals

  • Replacing authentication, identity proofing, or session management.
  • Choosing an agent model, planner, prompt, or vendor.
  • Replacing a database or defining data ownership.
  • Defining a universal visual component library.
  • Assuming that UI visibility is authorization.
  • Allowing an old authorization result to survive a policy change.

2. Core concepts

Resource

Something that exists

A data object, collection, file, tool, workflow, view, model, agent, or identity.

Principal

Something that acts

A person, agent, service, group, or organization. A delegated agent remains distinct from its delegator.

Capability

A typed possible action

A named operation with inputs, outputs, side effects, risk, and required policy.

Policy

A rule about access

An allow or deny rule connecting a principal, operation, resource, scope, and conditions.

Projection

A safe authorized view

The subset of resources, fields, and actions that may be disclosed to a consumer.

Receipt

Evidence of a decision

An append-only record of authorization, confirmation, execution, and outcome.

Normative rule: a generated interface is a presentation of an authorized projection. It is never the source of authorization.

3. Data model

All identifiers are namespaced strings. Resource schemas use JSON Schema 2020-12 or a compatible typed schema. Unknown fields are rejected by default.

interface Subject { id: string; // user:john, agent:personal type: 'user' | 'agent' | 'service' | 'group'; authenticated: boolean; attributes: Record<string, unknown>; } interface Resource { id: string; // calendar:john type: 'data' | 'capability' | 'workflow' | 'view' | 'agent'; version: string; // changes when schema or semantics change schema: JsonSchema; sensitivity: 'public' | 'personal' | 'confidential' | 'restricted'; owner?: string; metadata: Record<string, unknown>; } interface Capability extends Resource { type: 'capability'; operation: Operation; inputSchema: JsonSchema; outputSchema: JsonSchema; sideEffects: SideEffect[]; risk: 'low' | 'medium' | 'high' | 'critical'; confirmation: ConfirmationMode; idempotency: 'none' | 'supported' | 'required'; reversibility: 'reversible' | 'partially_reversible' | 'irreversible'; } type Operation = | 'discover' | 'inspect' | 'read' | 'create' | 'update' | 'delete' | 'execute' | 'share' | 'delegate';

3.1 Resource and capability separation

A tool is a resource whose operation is usually execute. Its schema can be inspectable even when invocation is denied. A data resource can expose read capabilities and mutation capabilities separately.

PermissionDisclosesDoes not imply
discoverThat a resource existsSchema, contents, or use
inspectMetadata, schema, risk, side effectsContents or execution
readContents, subject to field filtersMutation or sharing
executeInvocation of a named capabilityAccess to implementation data
sharePermission to grant access onwardPermission to use the resource personally

4. Policy language

Policies are declarative. The reference evaluator is deterministic, side-effect free, and usable without an agent or network call.

interface Policy { id: string; effect: 'allow' | 'deny'; principal: SubjectSelector; operation: Operation | Operation[]; resource: ResourceSelector; scope?: ScopeExpression; conditions?: ConditionExpression[]; obligations?: Obligation[]; priority: number; validDuring?: TimeWindow; version: string; } interface AuthorizationRequest { requestId: string; subject: Subject; operation: Operation; resource: ResourceRef; proposedInput?: unknown; purpose?: string; context: RequestContext; } interface Decision { requestId: string; effect: 'allow' | 'deny'; reasonCode: string; matchedPolicies: string[]; obligations: Obligation[]; fieldFilter?: FieldFilter; expiresAt?: string; policyVersion: string; }

4.1 Conditions

Subject and scope

subjectIs, roleIs, ownerIsSubject, resourceInWorkspace, fieldsWithin

Context

purposeIs, approvalPresent, timeBetween, deviceTrustAtLeast, mfaRecent

Input constraints

recipientCountAtMost, amountAtMost, domainIs, queryContainsNoSecrets

Obligations

requireConfirmation, previewChanges, redactFields, writeReceipt, requireHumanReview

policy.allow({ id: 'john-send-own-mail', principal: subject('user:john'), operation: 'execute', resource: capability('mail.send'), scope: { mailbox: 'user:john' }, conditions: [ purposeIs('job_outreach'), recipientCountAtMost(20), approvalPresent('this_action') ], obligations: [writeReceipt(), requireConfirmation('explicit')] });

5. Policy evaluation algorithm

The evaluator must return the same result for the same policy set, request, and context. A deny result is the default.

Normalize request
Check identity
Match rules
Resolve conflict
Apply obligations
Return decision
  1. Validate the request schema and reject unknown operations or malformed resource references.
  2. Require an authenticated subject for every operation except explicitly public discovery.
  3. Resolve the resource and its current version.
  4. Collect policies matching subject, operation, resource, scope, and validity window.
  5. Evaluate conditions against the current request context and proposed input.
  6. Apply deny-overrides: a matching deny wins over an allow at the same effective priority.
  7. Apply priority ordering. A more specific rule outranks a broader rule only when the policy explicitly declares its priority.
  8. Return allow only when at least one applicable allow remains and no applicable deny overrides it.
  9. Attach obligations, field filters, expiration, policy version, and machine-readable reason code.
Fail-closed behavior: unavailable policy services, stale policy versions, invalid input, missing identity, and ambiguous rule conflicts produce denial for protected resources.

6. Reference API

The library is split into pure core contracts and adapters. A host application can use only the parts it needs.

const cup = createCapabilityUI({ registry, policy: policyEngine, receipts: receiptSink, clock: systemClock, nonce: nonceProvider }); // 1. Register typed resources and tools. cup.register(calendarResource); cup.register(createEventCapability); // 2. Ask for the safe view given the user's goal. const view = await cup.project({ subject: john, goal: 'plan a team meeting', context: requestContext }); // 3. Give only this view to the agent or renderer. const response = await agent.compose({ goal, authorizedView: view }); // 4. Re-authorize the exact action at the side-effect boundary. const receipt = await cup.execute({ subject: john, capability: 'calendar.create_event', input: response.action.input, confirmation: response.action.confirmation, context: requestContext });

6.1 Public interfaces

Registry

register(resource) · get(id,version) · listDiscoverable(request) · validate(id,input)

Policy engine

authorize(request) · explain(request) · policyVersion() · invalidate(cacheKey)

Projector

project(request) · redact(resource,decision) · buildActionSchema(capability)

Executor

prepare(request) · confirm(token) · execute(request) · reverse(receiptId)

7. Authorized projection

The projector converts policy decisions into an agent-safe document. It includes only what the consumer may see and do.

interface AuthorizedView { viewId: string; subjectId: string; purpose?: string; generatedAt: string; policyVersion: string; resources: AuthorizedResource[]; globalObligations: Obligation[]; } interface AuthorizedResource { ref: ResourceRef; visibility: 'listed' | 'inspectable' | 'readable' | 'usable'; schema?: JsonSchema; data?: unknown; fields?: FieldPermission[]; capabilities: AuthorizedCapability[]; } interface AuthorizedCapability { id: string; inputSchema: JsonSchema; outputSchema: JsonSchema; risk: Risk; sideEffects: SideEffect[]; obligations: Obligation[]; actionToken: string; // opaque, short-lived, audience-bound }

7.1 Field-level permissions

Field filters are applied after authorization and before projection. A caller may read a contact record while receiving only name and organization. Redaction must be structural, not a string replacement that can leak through derived fields.

Projection stateAgent receivesRenderer may show
HiddenNothingNothing
ListedStable ref and safe labelExistence and label
InspectableSchema, risk, side effectsForm structure and warnings
ReadableFiltered contentsAuthorized fields
UsableShort-lived action tokenAction control with obligations

8. Execution and confirmation

Execution is a separate protocol from projection. The executor trusts neither a UI event nor an agent-produced payload.

Prepare

Validate capability ID, input schema, subject, purpose, current resource version, and action-token audience.

Confirm

For an explicit obligation, show the normalized action and side effects. Bind confirmation to the exact input hash.

Execute

Re-authorize immediately before calling the adapter. Enforce scope and input constraints again.

Receipt

Record policy version, input hash, confirmation, result, actor, and reversal reference.

Retry

Use idempotency keys for capabilities that can create duplicate external effects.

Reverse

Expose a separate reversal capability. Reversibility is never inferred from the original operation.

interface ExecutionRequest { subject: Subject; capability: string; input: unknown; actionToken?: string; confirmation?: Confirmation; idempotencyKey?: string; context: RequestContext; } interface Receipt { id: string; status: 'succeeded' | 'failed' | 'denied' | 'pending'; actor: SubjectRef; capability: string; resourceRefs: ResourceRef[]; inputHash: string; decision: Decision; confirmation?: Confirmation; resultSummary?: unknown; reversibleBy?: string; createdAt: string; }

9. Security model

Threats addressed

  • Prompt injection attempting to reveal hidden resources.
  • Agent hallucination of unavailable tools or fields.
  • Forged UI events and modified client payloads.
  • Confused-deputy actions through delegated agents.
  • Stale permissions after revocation.
  • Over-broad approval reused for another purpose.
  • Data leakage through derived output or error messages.

Required controls

  • Backend enforcement for every protected operation.
  • Opaque, audience-bound, short-lived action tokens.
  • Default deny and deny-overrides conflict resolution.
  • Policy version and resource version checks.
  • Input and output schema validation.
  • Purpose binding, expiration, and approval nonce binding.
  • Structured receipts with tamper-evident storage.

9.1 Trust boundaries

Identity provider
Host app
CUP policy core
Agent / renderer
CUP executor
External adapter

Boundary rule: agents and renderers are untrusted consumers of projections. External adapters are trusted only to perform the operation that the executor has already authorized.

10. Adapter contracts

Adapters isolate vendor and framework choices from the protocol.

IdentityAdapter

Resolves authenticated subjects and attributes. It cannot grant resource permissions.

ResourceAdapter

Loads current resource metadata, versions, schemas, and data. It applies ownership rules supplied by the host.

PolicyAdapter

Evaluates requests locally or through OPA, Cedar, a database, or a custom evaluator.

CapabilityAdapter

Executes a named operation after the executor has authorized it. It receives normalized input only.

RendererAdapter

Converts authorized schemas into React, Web Components, native controls, voice prompts, or other presentation.

ReceiptSink

Writes receipts to an append-only store and supports lookup by request, actor, resource, and capability.

interface CapabilityAdapter { capabilityId: string; invoke(input: unknown, ctx: ExecutionContext): Promise<unknown>; } interface RendererAdapter<T> { render(view: AuthorizedView, target: T): RenderResult; } interface ReceiptSink { append(receipt: Receipt): Promise<void>; find(query: ReceiptQuery): Promise<Receipt[]>; }

11. Conformance and testing

An implementation is CUP-conformant only when it passes semantic tests. Matching method names is insufficient.

Core conformance

  • Unknown operations are rejected.
  • No matching allow produces deny.
  • Matching deny overrides allow at equal priority.
  • Expiration and revocation take effect at execution.
  • Discovery never leaks protected metadata.
  • Field filters remove unauthorized values structurally.
  • Projection and execution produce the same policy version check.

Execution conformance

  • Modified input invalidates confirmation.
  • Wrong audience invalidates action tokens.
  • Missing idempotency key is rejected when required.
  • Adapters cannot be called after a denied decision.
  • Every required receipt is written before success returns.
  • Reversal uses a distinct authorized capability.
  • Policy failures fail closed for protected resources.

11.1 Reference fixture

test('other user cannot send from John mailbox', async () => { const decision = await engine.authorize({ subject: otherUser, operation: 'execute', resource: ref('mail.send'), proposedInput: { mailbox: 'user:john', recipients: ['x@example.com'] }, purpose: 'job_outreach', context }); expect(decision.effect).toBe('deny'); expect(adapter.invoke).not.toHaveBeenCalled(); });

12. Release plan and open decisions

v0.1 · semantic core

TypeScript types, pure evaluator, JSON Schema validation, projections, action tokens, receipts, and conformance fixtures.

v0.2 · production adapters

PostgreSQL resource adapter, OPA or Cedar bridge, React renderer contract, Web Components renderer, and policy simulator.

v0.3 · delegation

Delegation chains, capability attenuation, organization policy, approval workflows, and cross-service receipts.

Decisions requiring field experience

QuestionCurrent proposalEvidence needed
Policy languageTyped builder API plus portable JSON representationInteroperability tests across evaluators
Action tokensOpaque, short-lived, audience-bound tokensOperational latency and revocation requirements
Policy conflictDeny-overrides with explicit priorityEnterprise policy authoring studies
UI contractAuthorizedView independent of visual frameworkReact, native, voice, and accessibility implementations
Receipt storageAppend-only sink owned by host applicationAudit, privacy, retention, and redaction requirements
Recommended first build: implement the pure evaluator and execution guard before building a broad renderer ecosystem. The protocol’s value depends on the enforcement contract, not the visual novelty of the generated interface.