As explained in #1219 Channel object have no traits field:
Proposal: Channel Traits (RFC Stage 1)
PR body + concrete edits for asyncapi/spec, source of truth spec/asyncapi.md.
Target release: 3.2.0 (next minor, non-breaking — new optional field).
This proposal adds traits to the Channel Object, closing the only gap left after Operation Traits and Message Traits. It standardizes channel binding composition and removes the combinatorial explosion of pre-built per-combination channels.
Edit 1 — Channel Object: add the traits field
In the Channel Object fixed-fields table in spec/asyncapi.md, add this row (place it last, after bindings):
| <a name="channelObjectTraits"></a>traits | [[Channel Trait Object](#channelTraitObject) | [Reference Object](#referenceObject)] | A list of traits to apply to the channel object. Traits MUST be merged using [traits merge mechanism](#traitsMergeMechanism). The resulting object MUST be a valid [Channel Object](#channelObject). |
Copy the exact wording and anchor from the existing Message Object traits row in the same file so the merge reference (#traitsMergeMechanism vs inline) matches whatever 3.1.0 already uses. Do not invent the anchor — mirror the sibling row verbatim.
Edit 2 — New section: Channel Trait Object
Insert a new section immediately after the Channel Object section (and its example), mirroring the Operation Trait Object / Message Trait Object sections:
#### <a name="channelTraitObject"></a>Channel Trait Object
Describes a trait that MAY be applied to a [Channel Object](#channelObject). This object MAY contain any property from the [Channel Object](#channelObject), except the `address`, `messages`, and `traits` properties.
If you're looking to apply traits to an operation, see the [Operation Trait Object](#operationTraitObject). If you're looking to apply traits to a message, see the [Message Trait Object](#messageTraitObject).
##### Fixed Fields
| Field Name | Type | Description |
|---|---|---|
| <a name="channelTraitObjectTitle"></a>title | `string` | A human-friendly title for the channel. |
| <a name="channelTraitObjectSummary"></a>summary | `string` | A short summary of the channel. |
| <a name="channelTraitObjectDescription"></a>description | `string` | An optional description of this channel. [CommonMark syntax](https://spec.commonmark.org/) can be used for rich text representation. |
| <a name="channelTraitObjectServers"></a>servers | [[Reference Object](#referenceObject)] | An array of `$ref` pointers to the definition of the [Servers](#serverObject) in which this channel is available. If the channel is located in the [root Channels Object](#channelsObject), it MUST point to a subset of server definitions located in the [root Servers Object](#serversObject), and MUST NOT point to a subset of server definitions located in the [Components Object](#componentsObject) or anywhere else. If the channel is located in the [Components Object](#componentsObject), it MAY point to a [Server Objects](#serverObject) in any location. If `servers` is absent or empty, this channel MUST be available on all the servers defined in the [Servers Object](#serversObject). |
| <a name="channelTraitObjectParameters"></a>parameters | Map[`string`, [Parameter Object](#parameterObject) | [Reference Object](#referenceObject)] | A map of the parameters included in the channel address. It MUST be present only when the address contains [Channel Address Expressions](#channelAddressExpressions). |
| <a name="channelTraitObjectTags"></a>tags | [Tags Object](#tagsObject) | A list of tags for logical grouping of channels. |
| <a name="channelTraitObjectExternalDocs"></a>externalDocs | [External Documentation Object](#externalDocumentationObject) | [Reference Object](#referenceObject) | Additional external documentation for this channel. |
| <a name="channelTraitObjectBindings"></a>bindings | [Channel Bindings Object](#channelBindingsObject) | [Reference Object](#referenceObject) | A map where the keys describe the name of the protocol and the values describe protocol-specific definitions for the channel. |
This object MAY be extended with [Specification Extensions](#specificationExtensions).
##### Channel Trait Object Example
```yaml
bindings:
kafka:
partitions: 6
replicas: 3
```
Scope decision (resolve in review): the table above is the full mirror (channel minus address/messages/traits). If consensus lands on the minimal set, drop the servers and parameters rows and the corresponding JSON Schema properties. The motivating use case (binding composition) only requires bindings; title/summary/description/tags/externalDocs are kept for parity with Operation/Message Traits.
Edit 3 — Components Object: add channelTraits
In the Components Object fixed-fields table, add this row next to operationTraits and messageTraits:
| <a name="componentsObjectChannelTraits"></a>channelTraits | Map[`string`, [Channel Trait Object](#channelTraitObject) | [Reference Object](#referenceObject)] | An object to hold reusable [Channel Trait Objects](#channelTraitObject). |
If the Components Object has a worked example block, add a channelTraits entry alongside the existing operationTraits/messageTraits examples.
Edit 4 — Full worked example (for the prose / examples directory)
asyncapi: 3.2.0
info:
title: Account Service
version: 1.0.0
components:
channelTraits:
kafkaReplicated:
bindings:
kafka:
partitions: 6
replicas: 3
kafkaCompacted:
bindings:
kafka:
topicConfiguration:
cleanup.policy: [compact]
messages:
UserSignedUp:
payload:
type: object
properties:
userId:
type: string
channels:
userSignedUp:
address: user.signedup
traits:
- $ref: '#/components/channelTraits/kafkaReplicated'
- $ref: '#/components/channelTraits/kafkaCompacted'
messages:
userSignedUp:
$ref: '#/components/messages/UserSignedUp'
After merge, userSignedUp resolves to a channel with partitions: 6, replicas: 3, and cleanup.policy: [compact] — composed from two orthogonal traits instead of a replicated-compacted channel definition.
Implementation checklist (files per repo)
asyncapi/spec
asyncapi/spec-json-schemas (required to reach Stage 2 Draft)
asyncapi/parser-js (required to reach Stage 2 Draft, fully merged for Stage 3 Accepted)
As explained in #1219 Channel object have no
traitsfield:Proposal: Channel Traits (RFC Stage 1)
This proposal adds traits to the Channel Object, closing the only gap left after Operation Traits and Message Traits. It standardizes channel binding composition and removes the combinatorial explosion of pre-built per-combination channels.
Edit 1 — Channel Object: add the
traitsfieldIn the Channel Object fixed-fields table in
spec/asyncapi.md, add this row (place it last, afterbindings):Edit 2 — New section: Channel Trait Object
Insert a new section immediately after the Channel Object section (and its example), mirroring the Operation Trait Object / Message Trait Object sections:
Edit 3 — Components Object: add
channelTraitsIn the Components Object fixed-fields table, add this row next to
operationTraitsandmessageTraits:If the Components Object has a worked example block, add a
channelTraitsentry alongside the existingoperationTraits/messageTraitsexamples.Edit 4 — Full worked example (for the prose / examples directory)
After merge,
userSignedUpresolves to a channel withpartitions: 6,replicas: 3, andcleanup.policy: [compact]— composed from two orthogonal traits instead of areplicated-compactedchannel definition.Implementation checklist (files per repo)
asyncapi/specspec/asyncapi.md— Edits 1–4 above (this is the only source-of-truth file).docker run -v $PWD:/workdir ghcr.io/igorshubovych/markdownlint-cli:v0.35.0 "spec/asyncapi.md"asyncapi/spec-json-schemas(required to reach Stage 2 Draft)definitions/3.1.0/→definitions/3.2.0/, then search-replace3.1.0→3.2.0inside the new folder.examples/3.1.0/→examples/3.2.0/(same version replace); add a channel-traits example.definitions/3.2.0/channelTrait.json(seechannelTrait.jsonattached — but copy the exact sub-schema$reffilenames from the siblingchannel.jsonin the same folder; don't trust the names blindly).definitions/3.2.0/channel.json— add atraitsproperty:definitions/3.2.0/components.json— add achannelTraitsmap mirroringoperationTraits/messageTraits(values:oneOf[Reference, channelTrait]).index.jsandindex.d.ts— register3.2.0(and the-without-$idvariant) exactly as prior versions are registered.schemas/3.2.0.jsonandschemas/3.2.0-without-$id.jsonare generated bytools/bundler— run the bundler, don't hand-edit them.asyncapi/parser-js(required to reach Stage 2 Draft, fully merged for Stage 3 Accepted)Channel.traits()accessor / model methods consistent withOperation/Message.channelTraits+ inlinetraits, asserting the merged channel and binding composition.