PermDock
Standards

CloudEvents 1.0.2

The CNCF CloudEvents envelope PermDock wraps every sink event in, the closed com.permdock type list, the JSON batch format webhooks carry, and the distributed tracing extension.

What it is

CloudEvents is a CNCF specification for describing an event in a common envelope. Version 1.0.2 defines the context attributes every event carries (specversion, id, source, type) and the optional ones (subject, time, datacontenttype, dataschema). The JSON event format serialises one event as application/cloudevents+json and a batch as a JSON array, application/cloudevents-batch+json. The distributed tracing extension adds W3C Trace Context traceparent and tracestate.

Why it matters for PermDock

  • One envelope for every event. Decision, approval, directory, membership, credential and support-session events leave the library in the same shape, so a queue, a webhook receiver or a log pipeline routes on type without reading data.
  • Receivers already speak it. Event buses, Knative, Azure Event Grid and most queue consumers accept CloudEvents, so a DecisionSink needs no PermDock code on the far side.
  • The data is typed and published. dataschema points at the JSON Schema of the event family, so a receiver in another language validates data without guessing.

How PermDock uses it

toCloudEvent wraps one sink event. source is required and must be an absolute URI; the spec allows a URI-reference, PermDock does not, because a relative source cannot name the producer once the event leaves the app.

import type { DecisionSink } from "permdock";
import { CLOUDEVENTS_BATCH_CONTENT_TYPE, toCloudEvent } from "permdock";

const sink: DecisionSink = {
  async write(events) {
    await fetch("https://events.example.com/ingest", {
      method: "POST",
      headers: { "content-type": CLOUDEVENTS_BATCH_CONTENT_TYPE },
      body: JSON.stringify(
        events.map((event) =>
          toCloudEvent(event, { source: "https://app.example.com" }),
        ),
      ),
    });
  },
};

A receiver validates the batch with parseCloudEventBatch from permdock, or one event with parseCloudEvent (also re-exported by permdock/cloud, typed as PermDockCloudEvent). Both never throw, and both return null for an event outside the closed type list, a reserved type, a dataschema that does not match the data, a data shape that fails its schema, or a __proto__, constructor or prototype key. One invalid event rejects the whole batch.

{
  "specversion": "1.0",
  "type": "com.permdock.decision",
  "source": "https://app.example.com",
  "subject": "post.read",
  "id": "4f6c2a7e-1d0b-4c55-9b7e-2f8f8a1c9d10",
  "time": "2026-01-01T00:00:00.000Z",
  "datacontenttype": "application/json",
  "dataschema": "https://permdock.com/schemas/decision-event-v1.json",
  "traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
  "data": {
    "type": "decision",
    "outcome": "granted",
    "permission": "post.read"
  }
}

data is abridged here; the full decision event is on wire formats.

Types

type is com.permdock.<family> or com.permdock.<family>.<operation>. cloudEventType(event) returns it for a sink event, and CLOUD_EVENT_TYPES lists every value.

TypeEmitted for
com.permdock.decisionA decision event without an approval phase
com.permdock.approval.requestedA decision event with phase: 'requested'
com.permdock.approval.resolvedA decision event with phase: 'resolved'
com.permdock.approval.expiredA decision event with phase: 'expired'
com.permdock.approval.cancelledA decision event with phase: 'cancelled'
com.permdock.directory.createdA SCIM create
com.permdock.directory.replacedA SCIM replace (PUT)
com.permdock.directory.patchedA SCIM patch
com.permdock.directory.deletedA SCIM delete
com.permdock.membership.addedmembershipEvent with operation: 'added'
com.permdock.membership.removedmembershipEvent with operation: 'removed'
com.permdock.membership.changedmembershipEvent with operation: 'changed'
com.permdock.credential.createdcredentialEvent with operation: 'created'
com.permdock.credential.usedcredentialEvent with operation: 'used'
com.permdock.credential.rotatedcredentialEvent with operation: 'rotated'
com.permdock.credential.revokedcredentialEvent with operation: 'revoked'
com.permdock.catalog.publishedThe Cloud, after permdock cloud push
com.permdock.catalog.driftedThe Cloud, when a publish breaks a live hosted grant
com.permdock.support-session.startedaccessEvent or supportSessionEvent with operation: 'started'
com.permdock.support-session.endedaccessEvent with operation: 'ended' or 'revoked'; data.operation tells them apart
com.permdock.automation.publishedautomationEvent with operation: 'published'
com.permdock.automation.changedautomationEvent with operation: 'changed': a new version
com.permdock.automation.revokedautomationEvent with operation: 'revoked'
com.permdock.automation.runautomationEvent with operation: 'run'; WorkflowPermDock.start writes it to sink
com.permdock.delivery.sentdeliveryEvent with operation: 'sent': a transport accepted one recipient's delivery
com.permdock.delivery.delivereddeliveryEvent with operation: 'delivered', from a provider webhook
com.permdock.delivery.bounceddeliveryEvent with operation: 'bounced', from a provider webhook
com.permdock.delivery.complaineddeliveryEvent with operation: 'complained', from a provider webhook
com.permdock.delivery.faileddeliveryEvent with operation: 'failed': a refused recipient or a transport error

RESERVED_CLOUD_EVENT_TYPES holds types named for a later release, which the parsers refuse until then. It is empty in this release.

Mapping table

CloudEvents attributePermDock value
specversion"1.0"
idoptions.id, or a random UUID; a webhook retry keeps it
sourceoptions.source, an absolute URI naming the emitting service
typecloudEventType(event)
subjectThe permission key (decision), the resource id (directory), the principal id (membership, support session), the credential id, the automation id or the delivery id
timeThe sink event's at, RFC 3339
datacontenttype"application/json"
dataschemaEVENT_SCHEMA_IDS[family], https://permdock.com/schemas/<family>-event-v1.json
traceparent, tracestateoptions.traceparent and options.tracestate; tracestate is dropped without a traceparent
dataThe sink event, or the catalog data for com.permdock.catalog.*

Webhooks carry a batch signed with Standard Webhooks. The offline evidence export, signDecisionBatch, puts the same CloudEvents in the events claim of a vnd.permdock.evidence+jwt JWS (wire formats).

Why

com.permdock is the reverse-DNS prefix the specification recommends for type, under the domain PermDock publishes its schemas on. One type per operation lets a subscription filter on type alone, which is the attribute every broker indexes. The list is closed so a receiver can reject an event it does not understand instead of acting on a guess.

Sources

Last updated on

On this page